Version 1.0.1
Scaling enabled Hybrid mode in addressing UI overhaul Clean up
This commit is contained in:
+24
-33
@@ -60,41 +60,32 @@ function drawBoxes(inputPixelArray, widthPixels, heightPixels) {
|
||||
}
|
||||
|
||||
function drawBackground() {
|
||||
const grid = document.createElement("div");
|
||||
grid.id = "grid";
|
||||
grid.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 20px);
|
||||
grid-template-rows: repeat(auto-fill, 20px);
|
||||
grid-gap: 0px;
|
||||
`;
|
||||
|
||||
const boxSize = 20;
|
||||
const boxCount = Math.ceil(window.innerWidth / boxSize) * Math.ceil(window.innerHeight / boxSize);;
|
||||
const grid = document.createElement("div");
|
||||
grid.id = "grid";
|
||||
grid.classList.add("grid-class");
|
||||
grid.style.cssText = "";
|
||||
|
||||
for (let i = 0; i < boxCount; i++) {
|
||||
const box = document.createElement("div");
|
||||
box.classList.add("box");
|
||||
box.style.backgroundColor = getRandomColor();
|
||||
grid.appendChild(box);
|
||||
}
|
||||
grid.style.zIndex = -1;
|
||||
document.body.appendChild(grid);
|
||||
const boxSize = 20;
|
||||
const boxCount = Math.ceil(window.innerWidth / boxSize) * Math.ceil(window.innerHeight / boxSize);;
|
||||
|
||||
for (let i = 0; i < boxCount; i++) {
|
||||
const box = document.createElement("div");
|
||||
box.classList.add("box");
|
||||
box.style.backgroundColor = getRandomColor();
|
||||
grid.appendChild(box);
|
||||
}
|
||||
|
||||
function getRandomColor() {
|
||||
const letters = "0123456789ABCDEF";
|
||||
let color = "rgba(";
|
||||
for (let i = 0; i < 3; i++) {
|
||||
color += Math.floor(Math.random() * 256) + ",";
|
||||
}
|
||||
color += "0.05)";
|
||||
return color;
|
||||
grid.style.zIndex = -1;
|
||||
document.body.appendChild(grid);
|
||||
}
|
||||
|
||||
function getRandomColor() {
|
||||
const letters = "0123456789ABCDEF";
|
||||
let color = "rgba(";
|
||||
for (let i = 0; i < 3; i++) {
|
||||
color += Math.floor(Math.random() * 256) + ",";
|
||||
}
|
||||
color += "0.05)";
|
||||
return color;
|
||||
}
|
||||
|
||||
window.drawBackground = drawBackground;
|
||||
|
||||
+61
-17
@@ -3,7 +3,7 @@ function getPixelRGBValues(base64Image) {
|
||||
const copyJSONledbutton = document.getElementById('copyJSONledbutton');
|
||||
const JSONled = document.getElementById('JSONled');
|
||||
const maxNoOfColorsInCommandSting = document.getElementById('colorLimitNumber').value;
|
||||
|
||||
let hybridAddressing = false;
|
||||
let selectedIndex = -1;
|
||||
|
||||
let selector = document.getElementById("formatSelector");
|
||||
@@ -23,9 +23,11 @@ function getPixelRGBValues(base64Image) {
|
||||
|
||||
selector = document.getElementById("addressingSelector");
|
||||
selectedIndex = selector.selectedIndex;
|
||||
let segmentValueCheck = true;
|
||||
let segmentValueCheck = true; //If Range or Hybrid
|
||||
if (selector.options[selectedIndex].value == 'single'){
|
||||
segmentValueCheck = false
|
||||
} else if (selector.options[selectedIndex].value == 'hybrid'){
|
||||
hybridAddressing = true;
|
||||
}
|
||||
|
||||
let segmentString = ''
|
||||
@@ -54,16 +56,29 @@ function getPixelRGBValues(base64Image) {
|
||||
|
||||
// Wait for the image to load before drawing it onto the canvas
|
||||
image.onload = function() {
|
||||
// Set the canvas size to the same as the image
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
imageInfo = '<p>Width: ' + image.width + ', Height: ' + image.height + ' (make sure this matches your led matrix setup)</p>'
|
||||
|
||||
let scalePath = document.getElementById("scalePath");
|
||||
let color = scalePath.getAttribute("fill");
|
||||
let sizeX = document.getElementById("sizeX").value;
|
||||
let sizeY = document.getElementById("sizeY").value;
|
||||
|
||||
if (color != accentColor || sizeX < 1 || sizeY < 1){
|
||||
//image will not be rezised Set desitred size to original size
|
||||
sizeX = image.width;
|
||||
sizeY = image.height;
|
||||
}
|
||||
|
||||
// Set the canvas size to the same as the desired image size
|
||||
canvas.width = sizeX;
|
||||
canvas.height = sizeY;
|
||||
|
||||
imageInfo = '<p>Width: ' + sizeX + ', Height: ' + image.height + ' (make sure this matches your led matrix setup)</p>'
|
||||
|
||||
// Draw the image onto the canvas
|
||||
context.drawImage(image, 0, 0);
|
||||
context.drawImage(image, 0, 0, sizeX, sizeY);
|
||||
|
||||
// Get the pixel data from the canvas
|
||||
var pixelData = context.getImageData(0, 0, image.width, image.height).data;
|
||||
var pixelData = context.getImageData(0, 0, sizeX, sizeY).data;
|
||||
|
||||
// Create an array to hold the RGB values of each pixel
|
||||
var pixelRGBValues = [];
|
||||
@@ -83,7 +98,7 @@ function getPixelRGBValues(base64Image) {
|
||||
var a = pixelData[i + 3];
|
||||
|
||||
let pixel = i/4
|
||||
let row = Math.floor(pixel/image.width);
|
||||
let row = Math.floor(pixel/sizeX);
|
||||
let led = pixel;
|
||||
if (ledSetupSelection == 'matrix'){
|
||||
//Do nothing, the matrix is set upp like the index in the image
|
||||
@@ -99,10 +114,10 @@ function getPixelRGBValues(base64Image) {
|
||||
//Setup is traditional zigzag
|
||||
//Row is right to left
|
||||
//Invert index of row for led
|
||||
let indexOnRow = led - (row * image.width);
|
||||
let maxIndexOnRow = image.width - 1;
|
||||
let indexOnRow = led - (row * sizeX);
|
||||
let maxIndexOnRow = sizeX - 1;
|
||||
let reversedIndexOnRow = maxIndexOnRow - indexOnRow;
|
||||
led = (row * image.width) + reversedIndexOnRow;
|
||||
led = (row * sizeX) + reversedIndexOnRow;
|
||||
}
|
||||
|
||||
// Add the RGB values to the pixel RGB values array
|
||||
@@ -152,19 +167,45 @@ function getPixelRGBValues(base64Image) {
|
||||
//Next pixel has new color
|
||||
//The current segment ends with this pixel
|
||||
segmentEnd = i + 1 //WLED wants the NEXT LED as the stop led...
|
||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
||||
if (segmentStart == i && hybridAddressing){
|
||||
//If only one led/pixel, no segment info needed
|
||||
if (JSONledString == ''){
|
||||
//If addressing is single, we need to start every command with a starting possition
|
||||
segmentString = '' + i + ',';
|
||||
//Fixed to b2
|
||||
} else{
|
||||
segmentString = ''
|
||||
}
|
||||
}
|
||||
else {
|
||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//This is the last pixel, so the segment must end
|
||||
segmentEnd = i + 1;
|
||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
||||
|
||||
if (segmentStart + 1 == segmentEnd && hybridAddressing){
|
||||
//If only one led/pixel, no segment info needed
|
||||
if (JSONledString == ''){
|
||||
//If addressing is single, we need to start every command with a starting possition
|
||||
segmentString = '' + i + ',';
|
||||
//Fixed to b2
|
||||
} else{
|
||||
segmentString = ''
|
||||
}
|
||||
}
|
||||
else {
|
||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
||||
}
|
||||
}
|
||||
} else{
|
||||
//Write every pixel
|
||||
if (JSONledString == ''){
|
||||
//If addressing is single, we ned to start every command with a starting possition
|
||||
JSONledString = i + ', \'dummy\',';
|
||||
//If addressing is single, we need to start every command with a starting possition
|
||||
JSONledString = i
|
||||
//Fixed to b2
|
||||
}
|
||||
|
||||
segmentStart = i
|
||||
@@ -188,6 +229,8 @@ function getPixelRGBValues(base64Image) {
|
||||
//do nothing, allready set
|
||||
}
|
||||
|
||||
// Check if start and end is the same, in which case remove
|
||||
|
||||
JSONledString = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;
|
||||
|
||||
curentColorIndex = curentColorIndex + 1; // We've just added a new color to the string so up the count with one
|
||||
@@ -247,7 +290,8 @@ function getPixelRGBValues(base64Image) {
|
||||
infoDiv.innerHTML = imageInfo;
|
||||
canvasDiv.style.display = "block"
|
||||
|
||||
|
||||
//Drawing the image
|
||||
drawBoxes(pixelRGBValues, image.width, image.width);
|
||||
drawBoxes(pixelRGBValues, sizeX, sizeY);
|
||||
}
|
||||
}
|
||||
+55
-73
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
@@ -13,7 +12,9 @@
|
||||
|
||||
<body>
|
||||
<div class = top-part>
|
||||
<h1>Led Matrix Pixel Art Converter</h1>
|
||||
<h1><svg style="width:32px;height:32px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12 16C13.1 16 14 16.9 14 18S13.1 20 12 20 10 19.1 10 18 10.9 16 12 16M12 10C13.1 10 14 10.9 14 12S13.1 14 12 14 10 13.1 10 12 10.9 10 12 10M12 4C13.1 4 14 4.9 14 6S13.1 8 12 8 10 7.1 10 6 10.9 4 12 4M6 16C7.1 16 8 16.9 8 18S7.1 20 6 20 4 19.1 4 18 4.9 16 6 16M6 10C7.1 10 8 10.9 8 12S7.1 14 6 14 4 13.1 4 12 4.9 10 6 10M6 4C7.1 4 8 4.9 8 6S7.1 8 6 8 4 7.1 4 6 4.9 4 6 4M18 16C19.1 16 20 16.9 20 18S19.1 20 18 20 16 19.1 16 18 16.9 16 18 16M18 10C19.1 10 20 10.9 20 12S19.1 14 18 14 16 13.1 16 12 16.9 10 18 10M18 4C19.1 4 20 4.9 20 6S19.1 8 18 8 16 7.1 16 6 16.9 4 18 4Z" />
|
||||
</svg>Led Matrix Pixel Art Converter</h1>
|
||||
<h2>Convert image to WLED JSON (pixel art on WLED matrix)</h2>
|
||||
<p>
|
||||
<table id="fieldTable" style="width: 100%; table-layout: fixed; align-content: center;">
|
||||
@@ -60,6 +61,7 @@
|
||||
<select id="addressingSelector">
|
||||
<option value="range" selected>Range (10, 17, #f4f4f4)</option>
|
||||
<option value="single">Single (#f4f4f4)</option>
|
||||
<option value="hybrid">Hybrid (#f0f0f0,10, 17, #f4f4f4)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -68,8 +70,8 @@
|
||||
<label for="brightnessNumber">Brightness:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle; display: flex; align-items: center;">
|
||||
<input type="range" id="brightnessNumber" min="1" max="255" value="127">
|
||||
<span id="brightnessValue">100</span>
|
||||
<input type="range" id="brightnessNumber" min="1" max="255" value="255">
|
||||
<span id="brightnessValue">255</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -94,7 +96,7 @@
|
||||
<label for="haUID">HA Device Unique ID:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="text" id="haUID" value="pixel_art_controller_001a">
|
||||
<input class="fullTextField" type="text" id="haUID" value="pixel_art_controller_001a">
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ha-hide">
|
||||
@@ -102,7 +104,7 @@
|
||||
<label for="haName">HA Device Name:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="text" id="haName" value="Pixel Art Kitchen">
|
||||
<input class="fullTextField" type="text" id="haName" value="Pixel Art Kitchen">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -110,61 +112,35 @@
|
||||
<label for="curlUrl">Device IP/host name:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="text" id="curlUrl" value="">
|
||||
<input class="fullTextField" type="text" id="curlUrl" value="">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class= "scaleTableClass" id="scaleTable" style="width: 100%; table-layout: fixed; align-content: center;">
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="renderCheckbox">Show pixel rendering</label>
|
||||
<div id="scaleDiv">
|
||||
<svg id="scaleToggle" style="width:36px;height:36px" viewBox="0 0 24 24" onclick="switchScale()">
|
||||
<path id="scaleTogglePath" fill="currentColor" d="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z" />
|
||||
</svg>
|
||||
|
||||
<svg id="scaleSvg" style="width:36px;height:36px" viewBox="0 0 24 24" onclick="switchScale()">
|
||||
<path id="scalePath" fill="currentColor" d="M21,15H23V17H21V15M21,11H23V13H21V11M23,19H21V21C22,21 23,20 23,19M13,3H15V5H13V3M21,7H23V9H21V7M21,3V5H23C23,4 22,3 21,3M1,7H3V9H1V7M17,3H19V5H17V3M17,19H19V21H17V19M3,3C2,3 1,4 1,5H3V3M9,3H11V5H9V3M5,3H7V5H5V3M1,11V19A2,2 0 0,0 3,21H15V11H1M3,19L5.5,15.79L7.29,17.94L9.79,14.72L13,19H3Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="checkbox" id="renderCheckbox" checked>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="helpCheckbox">Show help/about</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="checkbox" id="helpCheckbox">
|
||||
<div id="sizeDiv" style="display: none;">
|
||||
<label for="sizeX">X : </label> <input class="sizeInputFields" type="number" id="sizeX" value="0">
|
||||
|
||||
<label for="sizeY">Y : </label> <input class="sizeInputFields" type="number" id="sizeY" value="0">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<div id="help-container" style="display: none">>
|
||||
<p>
|
||||
1a. Create a GIF or PNG ( <a href="https://www.pixilart.com/" target="_blank">www.pixilart.com</a> ) <br>
|
||||
1b. Download some image that fits your led matrix ( <a href="https://www.spriters-resource.com/" target="_blank">www.spriters-resource.com</a>) <br>
|
||||
2. Upload the image using the file picker or drag-and-drop it <br>
|
||||
3. Select the led setup that matches your WLED. ( <a href="https://kno.wled.ge/advanced/mapping/" target="_blank">https://kno.wled.ge</a>) <br>
|
||||
4. Select Otput format. <br>
|
||||
- WLED is pure JSON in the way the documentation descripbes it.<br> Use in WLED (presets?)<br>
|
||||
- CURL is formated as a curl command you can past into a command <br> window and test yor led matrix<br>
|
||||
- Home Assistant is teh full YAML you can past into your <br> configuration.yaml in Home Assistant<br>
|
||||
5. Select Color format. Select HEX if possible (more efficiant)<br>
|
||||
6. Select Addressing scheme. Send all pixels individually or try to <br> send ranges of the same color.<br>
|
||||
7. Select brighness value<br>
|
||||
8. According to docs WLED can handle max 256 colors/command. So the JSON is <br> split if you have larger images. <br> Lower this value if you have issues.<br>
|
||||
9. Set Home Assistant Device values if you are going to use HA<br>
|
||||
10. Set the device IP/host for HA and CURL to work<br>
|
||||
11. Press the convert button <br>
|
||||
12. Copy the generated JSON and put it somewhere in WLED<br> , Run CURL or paste to Home Assistant
|
||||
</p>
|
||||
<p>
|
||||
This tool is a proof of concept and work in progress. As you might expect, there is absolutely no warranty, what so ever.
|
||||
</p>
|
||||
<p>
|
||||
The Arcade font is copyright (c) Jakob Fischer at www.pizzadude.dk, all rights reserved. Do not distribute without the author's permission.
|
||||
</p>
|
||||
<p>
|
||||
It should be said that I don‘t acctually own a setup for this. I basically did it to play around over new years. But I have ordered a matrix now, and a small controller chip... let's see how that works out.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<label for="file-picker">
|
||||
<div id="drop-zone">
|
||||
@@ -177,32 +153,31 @@
|
||||
<p>
|
||||
<input type="file" id="file-picker" style="display: none;">
|
||||
<div style="width: 100%; text-align: center;" >
|
||||
<img id="preview" style="display: block; margin: 0 auto;"><br>
|
||||
<img id="preview" style="display: block; margin: 0 auto;">
|
||||
<img id="newimage" style="display: block; margin: 0 auto;"><br>
|
||||
</div>
|
||||
|
||||
<form id="form">
|
||||
<input id="submitConvert" type="submit" value="Convert to JSON for WLED" style="display: none">
|
||||
</form>
|
||||
<div id="submitConvertDiv" style="display: none;">
|
||||
<button id="convertbutton" class="buttonclass"></button>
|
||||
</div>
|
||||
|
||||
<div id="raw-image-container" style="display: none">
|
||||
<img id="image" src="" alt="RawImage image">
|
||||
</div>
|
||||
|
||||
</p>
|
||||
|
||||
<div id="image-container" style="display: none">
|
||||
<div id="image-container" style="display: none;">
|
||||
<div id="image-info" style="display: none"></div>
|
||||
<p>
|
||||
<div>
|
||||
<textarea id="JSONled"></textarea>
|
||||
<br>
|
||||
<button id="copyJSONledbutton" >Copy led-JSON to Clipboard</button>
|
||||
<br>
|
||||
<button id="sendJSONledbutton" >Send to Device</button>
|
||||
<br>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
<textarea id="JSONled"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="button-container" style="display: none;">
|
||||
<button id="copyJSONledbutton" class="buttonclass"></button>
|
||||
<div class="gap"></div>
|
||||
<button id="sendJSONledbutton" class="buttonclass"></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id=bottom-part style="display: none" class=bottom-part></div>
|
||||
<canvas id="pixelCanvas"></canvas>
|
||||
@@ -210,9 +185,12 @@
|
||||
|
||||
|
||||
<script>
|
||||
//Code for dynamically loading the scripts as to prevent caching. Remove in production.
|
||||
function loadFiles(fileNames) {
|
||||
fileNames.forEach(function(fileName) {
|
||||
function loadFiles(fileNames, index) {
|
||||
if (index === fileNames.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fileName = fileNames[index];
|
||||
var fileExt = fileName.split('.').pop();
|
||||
var element;
|
||||
if (fileExt === 'js') {
|
||||
@@ -224,12 +202,16 @@
|
||||
element.rel = 'stylesheet';
|
||||
element.href = fileName + '?time=' + new Date().getTime();
|
||||
}
|
||||
|
||||
element.onload = function() {
|
||||
loadFiles(fileNames, index + 1);
|
||||
}
|
||||
|
||||
document.getElementsByTagName('head')[0].appendChild(element);
|
||||
});
|
||||
}
|
||||
|
||||
var files = ["statics.js", "getPixelValues.js", "boxdraw.js", "index.js", "styles.css"];
|
||||
loadFiles(files);
|
||||
var files = ["statics.js", "getPixelValues.js", "boxdraw.js", "index.js", "styles.css"];
|
||||
loadFiles(files, 0);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
+44
-24
@@ -1,28 +1,29 @@
|
||||
//Start up code
|
||||
console.log(location.host);
|
||||
document.getElementById('curlUrl').value = location.host;
|
||||
let httpArray = [];
|
||||
|
||||
|
||||
//On submit button pressed =======================
|
||||
document.getElementById('form').addEventListener('submit', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
document.getElementById("convertbutton").addEventListener("click", function() {
|
||||
|
||||
let base64Image = document.getElementById('preview').src;
|
||||
if (isValidBase64Gif(base64Image)) {
|
||||
document.getElementById('image').src = base64Image;
|
||||
getPixelRGBValues(base64Image);
|
||||
document.getElementById('image-container').style.display = "block"
|
||||
document.getElementById("button-container").style.display = "";
|
||||
|
||||
}
|
||||
else {
|
||||
let infoDiv = document.getElementById('image-info');
|
||||
let imageInfo = '<p><b>WARNING!</b> File does not appear to be a valid GIF image</p>'
|
||||
let imageInfo = '<p><b>WARNING!</b> File does not appear to be a valid image</p>'
|
||||
infoDiv.innerHTML = imageInfo;
|
||||
infoDiv.style.display = "block"
|
||||
document.getElementById('image-container').style.display = "none";
|
||||
document.getElementById('JSONled').value = '';
|
||||
console.log("The string '" + base64Image + "' is not a valid base64 GIF image.");
|
||||
console.log("The string '" + base64Image + "' is not a valid base64 image.");
|
||||
}
|
||||
|
||||
});
|
||||
@@ -30,12 +31,16 @@ document.getElementById('form').addEventListener('submit', function(event) {
|
||||
// Code for copying the generated string to clipboard
|
||||
|
||||
copyJSONledbutton.addEventListener('click', async () => {
|
||||
let JSONled = document.getElementById('JSONled');
|
||||
JSONled.select();
|
||||
try {
|
||||
await navigator.clipboard.writeText('test text');
|
||||
console.log('Text copied to clipboard');
|
||||
await navigator.clipboard.writeText(JSONled.value);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
try {
|
||||
await document.execCommand("copy");
|
||||
} catch (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -67,18 +72,6 @@ async function postPixels() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let helpCheckbox = document.getElementById("helpCheckbox");
|
||||
let helpDiv = document.getElementById("help-container");
|
||||
|
||||
helpCheckbox.addEventListener("change", function() {
|
||||
if (helpCheckbox.checked) {
|
||||
helpDiv.style.display = "block";
|
||||
} else {
|
||||
helpDiv.style.display = "none";
|
||||
}
|
||||
});
|
||||
|
||||
//File uploader code
|
||||
const dropZone = document.getElementById('drop-zone');
|
||||
const filePicker = document.getElementById('file-picker');
|
||||
@@ -136,7 +129,7 @@ function updatePreview(file) {
|
||||
reader.onload = function() {
|
||||
// Update the preview image
|
||||
preview.src = reader.result;
|
||||
document.getElementById("submitConvert").style.display = "block";
|
||||
document.getElementById("submitConvertDiv").style.display = "";
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
@@ -149,7 +142,7 @@ function isValidBase64Gif(string) {
|
||||
const base64jpgPattern = /^data:image\/jpg;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
||||
const base64webpPattern = /^data:image\/webp;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
||||
*/
|
||||
//REMOVED, Any image appear to work as long as it can be drawn to the canvas. Leavingg code in for future use, possibly
|
||||
//REMOVED, Any image appear to work as long as it can be drawn to the canvas. Leaving code in for future use, possibly
|
||||
if (1==1 || base64gifPattern.test(string) || base64pngPattern.test(string) || base64jpgPattern.test(string) || base64webpPattern.test(string)) {
|
||||
return true;
|
||||
}
|
||||
@@ -176,4 +169,31 @@ formatSelector.addEventListener("change", function() {
|
||||
for (var i = 0; i < hideableRows.length; i++) {
|
||||
hideableRows[i].classList.toggle("hide", this.value !== "ha");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function switchScale() {
|
||||
let scalePath = document.getElementById("scalePath");
|
||||
let scaleTogglePath = document.getElementById("scaleTogglePath");
|
||||
let color = scalePath.getAttribute("fill");
|
||||
let d = ''
|
||||
if (color === accentColor) {
|
||||
color = accentTextColor;
|
||||
d = scaleToggleOffd
|
||||
document.getElementById("sizeDiv").style.display = "none";
|
||||
// Set values to actual XY of image, if possible
|
||||
} else {
|
||||
color = accentColor;
|
||||
d = scaleToggleOnd
|
||||
document.getElementById("sizeDiv").style.display = "";
|
||||
}
|
||||
scalePath.setAttribute("fill", color);
|
||||
scaleTogglePath.setAttribute("fill", color);
|
||||
scaleTogglePath.setAttribute("d", d);
|
||||
}
|
||||
|
||||
document.getElementById("convertbutton").innerHTML =
|
||||
'<svg style="width:36px;height:36px" viewBox="0 0 24 24"><path fill="currentColor" d="M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z" /> </svg> Convert to WLED JSON ';
|
||||
document.getElementById("copyJSONledbutton").innerHTML =
|
||||
'<svg class="svg-icon" style="width:36px;height:36px" viewBox="0 0 24 24"> <path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /> </svg> Copy to clipboard';
|
||||
document.getElementById("sendJSONledbutton").innerHTML =
|
||||
'<svg class="svg-icon" style="width:36px;height:36px" viewBox="0 0 24 24"> <path fill="currentColor" d="M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z" /> </svg> Send to device';
|
||||
@@ -0,0 +1,949 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>Led Matrix Pixel Art Convertor</title>
|
||||
|
||||
<!-- not in use in minified version
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
-->
|
||||
<!--For minifyed-->
|
||||
<style>
|
||||
|
||||
.box {
|
||||
border: 2px solid white;
|
||||
}
|
||||
body {
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
background-color: #151515;
|
||||
|
||||
}
|
||||
|
||||
.top-part {
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.container {
|
||||
max-width: 100% -40px;
|
||||
border-radius: 0px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.3em;
|
||||
color: rgb(126, 76, 128);
|
||||
margin: 20px 0;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
line-height: 0.5;
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.3em;
|
||||
color: rgba(126, 76, 128, 0.61);
|
||||
margin: 20px 0;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
line-height: 0.5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
color: rgb(119, 119, 119);
|
||||
line-height: 1.5;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
}
|
||||
|
||||
#fieldTable {
|
||||
font-size: 1 em;
|
||||
color: #777;
|
||||
line-height: 1;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
}
|
||||
|
||||
#drop-zone {
|
||||
display: block;
|
||||
width: 100%-40px;
|
||||
border: 3px dashed #7E4C80;
|
||||
border-radius: 0px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
margin: 0px;
|
||||
cursor: pointer;
|
||||
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
|
||||
*.button {
|
||||
display: block;
|
||||
width: 100% - 40px;
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
margin: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#file-picker {
|
||||
display: none;
|
||||
}
|
||||
* select {
|
||||
background-color: #333333;
|
||||
color: #C0C0C0;
|
||||
border: 1px solid #C0C0C0;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0em;
|
||||
width: 100%;
|
||||
height: 27px;
|
||||
font-size: 15px;
|
||||
color: rgb(119, 119, 119);
|
||||
border-radius: 0;
|
||||
}
|
||||
* input[type=range] {
|
||||
-webkit-appearance:none;
|
||||
flex-grow: 1;
|
||||
border-radius: 0px;
|
||||
background: linear-gradient(to right, #333333 0%, #333333 100%);
|
||||
color: #C0C0C0;
|
||||
border: 1px solid #C0C0C0;
|
||||
margin-top: 0.5em;
|
||||
margin-left: 0em;
|
||||
}
|
||||
input[type="range"]::-webkit-slider-thumb{
|
||||
-webkit-appearance:none;
|
||||
width: 25px;
|
||||
height:25px;
|
||||
background:#7E4C80;
|
||||
position:relative;
|
||||
z-index:3;
|
||||
}
|
||||
.rangeNumber{
|
||||
width: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
* input[type=text] {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding-inline-start: 5px;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 27px;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
* input[type="checkbox"] {
|
||||
}
|
||||
* input[type=submit] {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 1.3em;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
* button {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 0.5em;
|
||||
margin-bottom: 15px;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 1.3em;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
* textarea {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 0em;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-radius: 0px;
|
||||
font-family: 'Courier', Arial, sans-serif;
|
||||
font-size: 1em;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
.gridstyle{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 20px);
|
||||
grid-template-rows: repeat(auto-fill, 20px);
|
||||
grid-gap: 0px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class = top-part>
|
||||
<h1>Led Matrix Pixel Art Converter</h1>
|
||||
<h2>Convert image to WLED JSON (pixel art on WLED matrix)</h2>
|
||||
<p>
|
||||
<table id="fieldTable" style="width: 100%; table-layout: fixed; align-content: center;">
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="ledSetupSelector">Led setup:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<select id="ledSetupSelector">
|
||||
<option value="matrix" selected>2D Matrix</option>
|
||||
<option value="r2l">Serpentine, first row right to left <-</option>
|
||||
<option value="l2r">Serpentine, first row left to right -></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="formatSelector">Output format:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<select id="formatSelector">
|
||||
<option value="wled" selected>WLED JSON</option>
|
||||
<option value="curl">CURL</option>
|
||||
<option value="ha">Home Assistant YAML</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="colorFormatSelector">Color code format:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<select id="colorFormatSelector">
|
||||
<option value="hex" selected>HEX (#f4f4f4)</option>
|
||||
<option value="dec">DEC (244,244,244)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="addressingSelector">Addressing:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<select id="addressingSelector">
|
||||
<option value="range" selected>Range (10, 17, #f4f4f4)</option>
|
||||
<option value="single">Single (#f4f4f4)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="brightnessNumber">Brightness:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle; display: flex; align-items: center;">
|
||||
<input type="range" id="brightnessNumber" min="1" max="255" value="127">
|
||||
<span id="brightnessValue">100</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="colorLimitNumber">Max no of colors/JSON:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle; display: flex; align-items: center;">
|
||||
<input type="range" id="colorLimitNumber" min="1" max="512" value="256">
|
||||
<span id="colorLimitValue" >256</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ha-hide">
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="haID">HA Device ID:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="text" id="haID" value="pixel_art_controller_001">
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ha-hide">
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="haUID">HA Device Unique ID:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="text" id="haUID" value="pixel_art_controller_001a">
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ha-hide">
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="haName">HA Device Name:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="text" id="haName" value="Pixel Art Kitchen">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="curlUrl">Device IP/host name:</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="text" id="curlUrl" value="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="renderCheckbox">Show pixel rendering</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="checkbox" id="renderCheckbox" checked>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<label for="helpCheckbox">Show help/about</label>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
<input type="checkbox" id="helpCheckbox">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<div id="help-container" style="display: none">>
|
||||
<p>
|
||||
1a. Create a GIF or PNG ( <a href="https://www.pixilart.com/" target="_blank">www.pixilart.com</a> ) <br>
|
||||
1b. Download some image that fits your led matrix ( <a href="https://www.spriters-resource.com/" target="_blank">www.spriters-resource.com</a>) <br>
|
||||
2. Upload the image using the file picker or drag-and-drop it <br>
|
||||
3. Select the led setup that matches your WLED. ( <a href="https://kno.wled.ge/advanced/mapping/" target="_blank">https://kno.wled.ge</a>) <br>
|
||||
4. Select Otput format. <br>
|
||||
- WLED is pure JSON in the way the documentation descripbes it.<br> Use in WLED (presets?)<br>
|
||||
- CURL is formated as a curl command you can past into a command <br> window and test yor led matrix<br>
|
||||
- Home Assistant is teh full YAML you can past into your <br> configuration.yaml in Home Assistant<br>
|
||||
5. Select Color format. Select HEX if possible (more efficiant)<br>
|
||||
6. Select Addressing scheme. Send all pixels individually or try to <br> send ranges of the same color.<br>
|
||||
7. Select brighness value<br>
|
||||
8. According to docs WLED can handle max 256 colors/command. So the JSON is <br> split if you have larger images. <br> Lower this value if you have issues.<br>
|
||||
9. Set Home Assistant Device values if you are going to use HA<br>
|
||||
10. Set the device IP/host for HA and CURL to work<br>
|
||||
11. Press the convert button <br>
|
||||
12. Copy the generated JSON and put it somewhere in WLED<br> , Run CURL or paste to Home Assistant
|
||||
</p>
|
||||
<p>
|
||||
This tool is a proof of concept and work in progress. As you might expect, there is absolutely no warranty, what so ever.
|
||||
</p>
|
||||
<p>
|
||||
The Arcade font is copyright (c) Jakob Fischer at www.pizzadude.dk, all rights reserved. Do not distribute without the author's permission.
|
||||
</p>
|
||||
<p>
|
||||
It should be said that I don‘t acctually own a setup for this. I basically did it to play around over new years. But I have ordered a matrix now, and a small controller chip... let's see how that works out.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<label for="file-picker">
|
||||
<div id="drop-zone">
|
||||
Drop image here <br>or <br>
|
||||
Click to select a file
|
||||
</div>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="file" id="file-picker" style="display: none;">
|
||||
<div style="width: 100%; text-align: center;" >
|
||||
<img id="preview" style="display: block; margin: 0 auto;"><br>
|
||||
</div>
|
||||
|
||||
<form id="form">
|
||||
<input id="submitConvert" type="submit" value="Convert to JSON for WLED" style="display: none">
|
||||
</form>
|
||||
|
||||
<div id="raw-image-container" style="display: none">
|
||||
<img id="image" src="" alt="RawImage image">
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div id="image-container" style="display: none">
|
||||
<div id="image-info" style="display: none"></div>
|
||||
<p>
|
||||
<div>
|
||||
<textarea id="JSONled"></textarea>
|
||||
<br>
|
||||
<button id="copyJSONledbutton" >Copy led-JSON to Clipboard</button>
|
||||
<br>
|
||||
<button id="sendJSONledbutton" >Send to Device</button>
|
||||
<br>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id=bottom-part style="display: none" class=bottom-part></div>
|
||||
<canvas id="pixelCanvas"></canvas>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
// STATICS.JS
|
||||
var curlStart = 'curl -X POST "http://';
|
||||
var curlMid1 = '/json/state" -d \'';
|
||||
var curlEnd = '\' -H "Content-Type: application/json"';
|
||||
|
||||
const haStart = '#Uncomment if you don\'t allready have these defined in your switch section of your configuration.yaml\n#- platform: command_line\n #switches:\n ';
|
||||
const haMid1 = '\n friendly_name: ';
|
||||
const haMid2 = '\n unique_id: ';
|
||||
const haMid3= '\n command_on: >\n ';
|
||||
const haMid4 = '\n command_off: >\n curl -X POST "http://';
|
||||
const haEnd = '/json/state" -d \'{"on":false}\' -H "Content-Type: application/json"';
|
||||
const haCommandLeading = ' ';
|
||||
|
||||
const JSONledStringStart = '{"on":true, "bri":';
|
||||
const JSONledStringMid1 = ', "seg":{"i":[';
|
||||
const JSONledShortStringStart = '{';
|
||||
const JSONledShortStringMid1 = '"seg":{"i":[';
|
||||
const JSONledStringEnd = ']}}';
|
||||
|
||||
// INDEX.JS
|
||||
|
||||
//Start up code
|
||||
console.log(location.host);
|
||||
document.getElementById('curlUrl').value = location.host;
|
||||
let httpArray = [];
|
||||
|
||||
|
||||
//On submit button pressed =======================
|
||||
document.getElementById('form').addEventListener('submit', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
let base64Image = document.getElementById('preview').src;
|
||||
if (isValidBase64Gif(base64Image)) {
|
||||
document.getElementById('image').src = base64Image;
|
||||
getPixelRGBValues(base64Image);
|
||||
document.getElementById('image-container').style.display = "block"
|
||||
}
|
||||
else {
|
||||
let infoDiv = document.getElementById('image-info');
|
||||
let imageInfo = '<p><b>WARNING!</b> File does not appear to be a valid GIF image</p>'
|
||||
infoDiv.innerHTML = imageInfo;
|
||||
infoDiv.style.display = "block"
|
||||
document.getElementById('image-container').style.display = "none";
|
||||
document.getElementById('JSONled').value = '';
|
||||
console.log("The string '" + base64Image + "' is not a valid base64 GIF image.");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Code for copying the generated string to clipboard
|
||||
|
||||
copyJSONledbutton.addEventListener('click', async () => {
|
||||
JSONled.select();
|
||||
try {
|
||||
await navigator.clipboard.writeText('test text');
|
||||
console.log('Text copied to clipboard');
|
||||
} catch (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
}
|
||||
});
|
||||
|
||||
sendJSONledbutton.addEventListener('click', async () => {
|
||||
if (window.location.protocol === "https:") {
|
||||
alert('Will only be available when served over http (or WLED is run over https)');
|
||||
} else {
|
||||
postPixels();
|
||||
}
|
||||
});
|
||||
|
||||
async function postPixels() {
|
||||
for (let i of httpArray) {
|
||||
try {
|
||||
console.log(i);
|
||||
console.log(i.length);
|
||||
const response = await fetch('http://'+document.getElementById('curlUrl').value+'/json/state', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
//'Content-Type': 'text/html; charset=UTF-8'
|
||||
},
|
||||
body: i
|
||||
});
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let helpCheckbox = document.getElementById("helpCheckbox");
|
||||
let helpDiv = document.getElementById("help-container");
|
||||
|
||||
helpCheckbox.addEventListener("change", function() {
|
||||
if (helpCheckbox.checked) {
|
||||
helpDiv.style.display = "block";
|
||||
} else {
|
||||
helpDiv.style.display = "none";
|
||||
}
|
||||
});
|
||||
|
||||
//File uploader code
|
||||
const dropZone = document.getElementById('drop-zone');
|
||||
const filePicker = document.getElementById('file-picker');
|
||||
const preview = document.getElementById('preview');
|
||||
|
||||
// Listen for dragenter, dragover, and drop events
|
||||
dropZone.addEventListener('dragenter', dragEnter);
|
||||
dropZone.addEventListener('dragover', dragOver);
|
||||
dropZone.addEventListener('drop', dropped);
|
||||
dropZone.addEventListener('click', zoneClicked);
|
||||
|
||||
// Listen for change event on file picker
|
||||
filePicker.addEventListener('change', filePicked);
|
||||
|
||||
// Handle zone click
|
||||
function zoneClicked(e) {
|
||||
e.preventDefault();
|
||||
//this.classList.add('drag-over');
|
||||
//alert('Hej');
|
||||
filePicker.click();
|
||||
}
|
||||
|
||||
// Handle dragenter
|
||||
function dragEnter(e) {
|
||||
e.preventDefault();
|
||||
this.classList.add('drag-over');
|
||||
}
|
||||
|
||||
// Handle dragover
|
||||
function dragOver(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Handle drop
|
||||
function dropped(e) {
|
||||
e.preventDefault();
|
||||
this.classList.remove('drag-over');
|
||||
|
||||
// Get the dropped file
|
||||
const file = e.dataTransfer.files[0];
|
||||
updatePreview(file);
|
||||
}
|
||||
|
||||
// Handle file picked
|
||||
function filePicked(e) {
|
||||
// Get the picked file
|
||||
const file = e.target.files[0];
|
||||
updatePreview(file);
|
||||
}
|
||||
|
||||
// Update the preview image
|
||||
function updatePreview(file) {
|
||||
// Use FileReader to read the file
|
||||
const reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
// Update the preview image
|
||||
preview.src = reader.result;
|
||||
document.getElementById("submitConvert").style.display = "block";
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function isValidBase64Gif(string) {
|
||||
// Use a regular expression to check that the string is a valid base64 string
|
||||
/*
|
||||
const base64gifPattern = /^data:image\/gif;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
||||
const base64pngPattern = /^data:image\/png;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
||||
const base64jpgPattern = /^data:image\/jpg;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
||||
const base64webpPattern = /^data:image\/webp;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
||||
*/
|
||||
//REMOVED, Any image appear to work as long as it can be drawn to the canvas. Leavingg code in for future use, possibly
|
||||
if (1==1 || base64gifPattern.test(string) || base64pngPattern.test(string) || base64jpgPattern.test(string) || base64webpPattern.test(string)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
//Not OK
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("brightnessNumber").oninput = function() {
|
||||
document.getElementById("brightnessValue").textContent = this.value;
|
||||
}
|
||||
|
||||
document.getElementById("colorLimitNumber").oninput = function() {
|
||||
document.getElementById("colorLimitValue").textContent = this.value;
|
||||
}
|
||||
|
||||
var formatSelector = document.getElementById("formatSelector");
|
||||
var hideableRows = document.querySelectorAll(".ha-hide");
|
||||
for (var i = 0; i < hideableRows.length; i++) {
|
||||
hideableRows[i].classList.add("hide");
|
||||
}
|
||||
formatSelector.addEventListener("change", function() {
|
||||
for (var i = 0; i < hideableRows.length; i++) {
|
||||
hideableRows[i].classList.toggle("hide", this.value !== "ha");
|
||||
}
|
||||
});
|
||||
|
||||
//GETPIXELVALUES.JS
|
||||
|
||||
function getPixelRGBValues(base64Image) {
|
||||
httpArray = [];
|
||||
const copyJSONledbutton = document.getElementById('copyJSONledbutton');
|
||||
const JSONled = document.getElementById('JSONled');
|
||||
const maxNoOfColorsInCommandSting = document.getElementById('colorLimitNumber').value;
|
||||
|
||||
let selectedIndex = -1;
|
||||
|
||||
let selector = document.getElementById("formatSelector");
|
||||
selectedIndex = selector.selectedIndex;
|
||||
const formatSelection = selector.options[selectedIndex].value;
|
||||
|
||||
selector = document.getElementById("ledSetupSelector");
|
||||
selectedIndex = selector.selectedIndex;
|
||||
const ledSetupSelection = selector.options[selectedIndex].value;
|
||||
|
||||
selector = document.getElementById("colorFormatSelector");
|
||||
selectedIndex = selector.selectedIndex;
|
||||
let hexValueCheck = true;
|
||||
if (selector.options[selectedIndex].value == 'dec'){
|
||||
hexValueCheck = false
|
||||
}
|
||||
|
||||
selector = document.getElementById("addressingSelector");
|
||||
selectedIndex = selector.selectedIndex;
|
||||
let segmentValueCheck = true;
|
||||
if (selector.options[selectedIndex].value == 'single'){
|
||||
segmentValueCheck = false
|
||||
}
|
||||
|
||||
let segmentString = ''
|
||||
let curlString = ''
|
||||
let haString = ''
|
||||
let haCommandCurlString = '';
|
||||
|
||||
|
||||
let colorSeparatorStart = '\'';
|
||||
let colorSeparatorEnd = '\'';
|
||||
if (!hexValueCheck){
|
||||
colorSeparatorStart = '[';
|
||||
colorSeparatorEnd = ']';
|
||||
}
|
||||
// Warnings
|
||||
let hasTransparency = false; //If alpha < 255 is detected on any pixel, this is set to true in code below
|
||||
let imageInfo = '';
|
||||
|
||||
// Create an off-screen canvas
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
|
||||
// Create an image element and set its src to the base64 image
|
||||
var image = new Image();
|
||||
image.src = base64Image;
|
||||
|
||||
// Wait for the image to load before drawing it onto the canvas
|
||||
image.onload = function() {
|
||||
// Set the canvas size to the same as the image
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
imageInfo = '<p>Width: ' + image.width + ', Height: ' + image.height + ' (make sure this matches your led matrix setup)</p>'
|
||||
|
||||
// Draw the image onto the canvas
|
||||
context.drawImage(image, 0, 0);
|
||||
|
||||
// Get the pixel data from the canvas
|
||||
var pixelData = context.getImageData(0, 0, image.width, image.height).data;
|
||||
|
||||
// Create an array to hold the RGB values of each pixel
|
||||
var pixelRGBValues = [];
|
||||
|
||||
// If the first row of the led matrix is right -> left
|
||||
let right2leftAdjust = 1;
|
||||
|
||||
if (ledSetupSelection == 'l2r'){
|
||||
right2leftAdjust = 0;
|
||||
}
|
||||
|
||||
// Loop through the pixel data and get the RGB values of each pixel
|
||||
for (var i = 0; i < pixelData.length; i += 4) {
|
||||
var r = pixelData[i];
|
||||
var g = pixelData[i + 1];
|
||||
var b = pixelData[i + 2];
|
||||
var a = pixelData[i + 3];
|
||||
|
||||
let pixel = i/4
|
||||
let row = Math.floor(pixel/image.width);
|
||||
let led = pixel;
|
||||
if (ledSetupSelection == 'matrix'){
|
||||
//Do nothing, the matrix is set upp like the index in the image
|
||||
//Every row starts from the left, i.e. no zigzagging
|
||||
}
|
||||
else if ((row + right2leftAdjust) % 2 === 0) {
|
||||
//Setup is traditional zigzag
|
||||
//right2leftAdjust basically flips the row order if = 1
|
||||
//Row is left to right
|
||||
//Leave led index as pixel index
|
||||
|
||||
} else {
|
||||
//Setup is traditional zigzag
|
||||
//Row is right to left
|
||||
//Invert index of row for led
|
||||
let indexOnRow = led - (row * image.width);
|
||||
let maxIndexOnRow = image.width - 1;
|
||||
let reversedIndexOnRow = maxIndexOnRow - indexOnRow;
|
||||
led = (row * image.width) + reversedIndexOnRow;
|
||||
}
|
||||
|
||||
// Add the RGB values to the pixel RGB values array
|
||||
pixelRGBValues.push([r, g, b, a, led, pixel, row]);
|
||||
}
|
||||
|
||||
pixelRGBValues.sort((a, b) => a[5] - b[5]);
|
||||
|
||||
//Copy the values to a new array for resorting
|
||||
let ledRGBValues = [... pixelRGBValues];
|
||||
|
||||
//Sort the array based on led index
|
||||
ledRGBValues.sort((a, b) => a[4] - b[4]);
|
||||
|
||||
//Generate JSON in WLED format
|
||||
let JSONledString = '';
|
||||
let JSONledStringShort = '';
|
||||
|
||||
//Set starting values for the segment check to something that is no color
|
||||
let segmentStart = -1;
|
||||
let maxi = ledRGBValues.length;
|
||||
let curentColorIndex = 0
|
||||
let commandArray = [];
|
||||
|
||||
//For evry pixel in the LED array
|
||||
for (let i = 0; i < maxi; i++) {
|
||||
let pixel = ledRGBValues[i];
|
||||
let r = pixel[0];
|
||||
let g = pixel[1];
|
||||
let b = pixel[2];
|
||||
let a = pixel[3];
|
||||
let segmentString = '';
|
||||
let segmentEnd = -1;
|
||||
|
||||
if(segmentValueCheck){
|
||||
if (segmentStart < 0){
|
||||
//This is the first led of a new segment
|
||||
segmentStart = i;
|
||||
} //Else we allready have a start index
|
||||
|
||||
if (i < maxi - 1){
|
||||
|
||||
let iNext = i + 1;
|
||||
let nextPixel = ledRGBValues[iNext];
|
||||
|
||||
if (nextPixel[0] != r || nextPixel[1] != g || nextPixel[2] != b ){
|
||||
//Next pixel has new color
|
||||
//The current segment ends with this pixel
|
||||
segmentEnd = i + 1 //WLED wants the NEXT LED as the stop led...
|
||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
||||
}
|
||||
|
||||
} else {
|
||||
//This is the last pixel, so the segment must end
|
||||
segmentEnd = i + 1;
|
||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
||||
}
|
||||
} else{
|
||||
//Write every pixel
|
||||
if (JSONledString == ''){
|
||||
//If addressing is single, we ned to start every command with a starting possition
|
||||
JSONledString = i + ', \'dummy\',';
|
||||
}
|
||||
|
||||
segmentStart = i
|
||||
segmentEnd = i
|
||||
//Segment string should be empty for when addressing single. So no need to set it again.
|
||||
}
|
||||
|
||||
if (a < 255){
|
||||
hasTransparency = true; //If ANY pixel has alpha < 255 then this is set to true to warn the user
|
||||
}
|
||||
|
||||
if (segmentEnd > -1){
|
||||
//This is the last pixel in the segment, write to the JSONledString
|
||||
//Return color value in selected format
|
||||
let colorValueString = r + ',' + g + ',' + b ;
|
||||
|
||||
if (hexValueCheck){
|
||||
const [red, green, blue] = [r, g, b];
|
||||
colorValueString = `${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`;
|
||||
} else{
|
||||
//do nothing, allready set
|
||||
}
|
||||
|
||||
JSONledString = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;
|
||||
|
||||
curentColorIndex = curentColorIndex + 1; // We've just added a new color to the string so up the count with one
|
||||
|
||||
if (curentColorIndex % maxNoOfColorsInCommandSting === 0 || i == maxi - 1) {
|
||||
|
||||
//If we have accumulated the max number of colors to send in a single command or if this is the last pixel, we should write the current colorstring to the array
|
||||
commandArray.push(JSONledString);
|
||||
JSONledString = ''; //Start on an new command string
|
||||
} else
|
||||
{
|
||||
//Add a comma to continue the command string
|
||||
JSONledString = JSONledString + ','
|
||||
}
|
||||
//Reset segment values
|
||||
segmentStart = - 1;
|
||||
}
|
||||
}
|
||||
|
||||
JSONledString = ''
|
||||
|
||||
//For evry commandString in the array
|
||||
for (let i = 0; i < commandArray.length; i++) {
|
||||
let thisJSONledString = JSONledStringStart + document.getElementById('brightnessNumber').value + JSONledStringMid1 + commandArray[i] + JSONledStringEnd;
|
||||
httpArray.push(thisJSONledString);
|
||||
|
||||
let thiscurlString = curlStart + document.getElementById('curlUrl').value + curlMid1 + thisJSONledString + curlEnd;
|
||||
|
||||
//Aggregated Strings That should be returned to the user
|
||||
if (i > 0){
|
||||
JSONledString = JSONledString + '\n';
|
||||
curlString = curlString + ' && ';
|
||||
}
|
||||
JSONledString = JSONledString + thisJSONledString;
|
||||
curlString = curlString + thiscurlString;
|
||||
}
|
||||
|
||||
|
||||
haString = haStart + document.getElementById('haID').value + haMid1 + document.getElementById('haName').value + haMid2 + document.getElementById('haUID').value + haMid3 +curlString + haMid3 + document.getElementById('curlUrl').value + haEnd;
|
||||
|
||||
if (formatSelection == 'wled'){
|
||||
JSONled.value = JSONledString;
|
||||
} else if (formatSelection == 'curl'){
|
||||
JSONled.value = curlString;
|
||||
} else if (formatSelection == 'ha'){
|
||||
JSONled.value = haString;
|
||||
} else {
|
||||
JSONled.value = 'ERROR!/n' + formatSelection + ' is an unknown format.'
|
||||
}
|
||||
|
||||
let infoDiv = document.getElementById('image-info');
|
||||
let canvasDiv = document.getElementById('image-info');
|
||||
if (hasTransparency){
|
||||
imageInfo = imageInfo + '<p><b>WARNING!</b> Transparency info detected in image. Transparency (alpha) has been ignored. To ensure you get the result you desire, use only solid colors in your image.</p>'
|
||||
}
|
||||
|
||||
infoDiv.innerHTML = imageInfo;
|
||||
canvasDiv.style.display = "block"
|
||||
|
||||
//Drawing the image
|
||||
drawBoxes(pixelRGBValues, image.width, image.width);
|
||||
}
|
||||
}
|
||||
|
||||
//BOXDRAW.JS
|
||||
|
||||
function drawBoxes(inputPixelArray, widthPixels, heightPixels) {
|
||||
|
||||
// Get a reference to the canvas element
|
||||
var canvas = document.getElementById('pixelCanvas');
|
||||
|
||||
// Get the canvas context
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
// Set the width and height of the canvas
|
||||
if (window.innerHeight < window.innerWidth) {
|
||||
canvas.width = Math.floor(window.innerHeight * 0.98);
|
||||
}
|
||||
else{
|
||||
canvas.width = Math.floor(window.innerWidth * 0.98);
|
||||
}
|
||||
//canvas.height = window.innerWidth;
|
||||
|
||||
let pixelSize = Math.floor(canvas.width/widthPixels);
|
||||
|
||||
//Set the canvas height to fit the right number of pixelrows
|
||||
canvas.height = (pixelSize * heightPixels) + 10
|
||||
|
||||
//Iterate through the matrix
|
||||
for (let y = 0; y < heightPixels; y++) {
|
||||
for (let x = 0; x < widthPixels; x++) {
|
||||
|
||||
// Calculate the index of the current pixel
|
||||
let i = (y*widthPixels) + x;
|
||||
|
||||
//Gets the RGB of the current pixel
|
||||
let pixel = inputPixelArray[i];
|
||||
|
||||
let pixelColor = 'rgb(' + pixel[0] + ', ' + pixel[1] + ', ' + pixel[2] + ')';
|
||||
let r = pixel[0];
|
||||
let g = pixel[1];
|
||||
let b = pixel[2];
|
||||
let pos = pixel[4];
|
||||
|
||||
let textColor = 'rgb(128,128,128)';
|
||||
|
||||
// Set the fill style to the pixel color
|
||||
ctx.fillStyle = pixelColor;
|
||||
|
||||
//Draw the rectangle
|
||||
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
||||
|
||||
// Draw a border on the box
|
||||
ctx.strokeStyle = '#888888';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
||||
|
||||
//Write text to box
|
||||
ctx.font = "10px Arial";
|
||||
ctx.fillStyle = textColor;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText((pos + 1), (x * pixelSize) + (pixelSize /2), (y * pixelSize) + (pixelSize /2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground() {
|
||||
const grid = document.createElement("div");
|
||||
grid.id = "grid";
|
||||
grid.classList.add("grid-class");
|
||||
grid.style.cssText = "";
|
||||
|
||||
const boxSize = 20;
|
||||
const boxCount = Math.ceil(window.innerWidth / boxSize) * Math.ceil(window.innerHeight / boxSize);;
|
||||
|
||||
for (let i = 0; i < boxCount; i++) {
|
||||
const box = document.createElement("div");
|
||||
box.classList.add("box");
|
||||
box.style.backgroundColor = getRandomColor();
|
||||
grid.appendChild(box);
|
||||
}
|
||||
grid.style.zIndex = -1;
|
||||
document.body.appendChild(grid);
|
||||
}
|
||||
|
||||
function getRandomColor() {
|
||||
const letters = "0123456789ABCDEF";
|
||||
let color = "rgba(";
|
||||
for (let i = 0; i < 3; i++) {
|
||||
color += Math.floor(Math.random() * 256) + ",";
|
||||
}
|
||||
color += "0.05)";
|
||||
return color;
|
||||
}
|
||||
|
||||
window.drawBackground = drawBackground;
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
+7
-1
@@ -15,4 +15,10 @@ const JSONledStringMid1 = ', "seg":{"i":[';
|
||||
const JSONledShortStringStart = '{';
|
||||
const JSONledShortStringMid1 = '"seg":{"i":[';
|
||||
const JSONledStringEnd = ']}}';
|
||||
|
||||
|
||||
var accentColor = '#7E4C80';
|
||||
var accentTextColor = '#777';
|
||||
|
||||
var scaleToggleOffd = "M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z";
|
||||
var scaleToggleOnd = "M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z";
|
||||
|
||||
|
||||
+225
-159
@@ -1,166 +1,232 @@
|
||||
|
||||
.box {
|
||||
border: 2px solid white;
|
||||
}
|
||||
body {
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
background-color: #151515;
|
||||
.box {
|
||||
border: 2px solid white;
|
||||
}
|
||||
body {
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
background-color: #151515;
|
||||
}
|
||||
|
||||
.top-part {
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.container {
|
||||
max-width: 100% -40px;
|
||||
border-radius: 0px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.3em;
|
||||
color: rgb(126, 76, 128);
|
||||
margin: 20px 0;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
line-height: 0.5;
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.3em;
|
||||
color: rgba(126, 76, 128, 0.61);
|
||||
margin: 20px 0;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
line-height: 0.5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
color: rgb(119, 119, 119);
|
||||
line-height: 1.5;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
}
|
||||
|
||||
#fieldTable {
|
||||
font-size: 1 em;
|
||||
color: #777;
|
||||
line-height: 1;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
}
|
||||
|
||||
#scaleTable {
|
||||
font-size: 1 em;
|
||||
color: #777;
|
||||
line-height: 1;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
}
|
||||
|
||||
#drop-zone {
|
||||
display: block;
|
||||
width: 100%-40px;
|
||||
border: 3px dashed #7E4C80;
|
||||
border-radius: 0px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
margin: 0px;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.top-part {
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.container {
|
||||
max-width: 100% -40px;
|
||||
border-radius: 0px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.3em;
|
||||
color: rgb(126, 76, 128);
|
||||
margin: 20px 0;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
line-height: 0.5;
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.3em;
|
||||
color: rgba(126, 76, 128, 0.61);
|
||||
margin: 20px 0;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
line-height: 0.5;
|
||||
text-align: center;
|
||||
}
|
||||
#file-picker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
color: rgb(119, 119, 119);
|
||||
line-height: 1.5;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
}
|
||||
|
||||
#fieldTable {
|
||||
font-size: 1 em;
|
||||
color: #777;
|
||||
line-height: 1;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
}
|
||||
* select {
|
||||
background-color: #333333;
|
||||
color: #C0C0C0;
|
||||
border: 1px solid #C0C0C0;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0em;
|
||||
width: 100%;
|
||||
height: 27px;
|
||||
font-size: 15px;
|
||||
color: rgb(119, 119, 119);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#drop-zone {
|
||||
display: block;
|
||||
width: 100%-40px;
|
||||
border: 3px dashed #7E4C80;
|
||||
border-radius: 0px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
margin: 0px;
|
||||
cursor: pointer;
|
||||
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
* input[type=range] {
|
||||
-webkit-appearance:none;
|
||||
flex-grow: 1;
|
||||
border-radius: 0px;
|
||||
background: linear-gradient(to right, #333333 0%, #333333 100%);
|
||||
color: #C0C0C0;
|
||||
border: 1px solid #C0C0C0;
|
||||
margin-top: 0.5em;
|
||||
margin-left: 0em;
|
||||
}
|
||||
|
||||
*.button {
|
||||
display: block;
|
||||
width: 100% - 40px;
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
margin: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
input[type="range"]::-webkit-slider-thumb{
|
||||
-webkit-appearance:none;
|
||||
width: 25px;
|
||||
height:25px;
|
||||
background:#7E4C80;
|
||||
position:relative;
|
||||
z-index:3;
|
||||
}
|
||||
|
||||
#file-picker {
|
||||
display: none;
|
||||
}
|
||||
* select {
|
||||
background-color: #333333;
|
||||
color: #C0C0C0;
|
||||
border: 1px solid #C0C0C0;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0em;
|
||||
width: 100%;
|
||||
height: 27px;
|
||||
font-size: 15px;
|
||||
color: rgb(119, 119, 119);
|
||||
border-radius: 0;
|
||||
}
|
||||
* input[type=range] {
|
||||
-webkit-appearance:none;
|
||||
flex-grow: 1;
|
||||
border-radius: 0px;
|
||||
background: linear-gradient(to right, #333333 0%, #333333 100%);
|
||||
color: #C0C0C0;
|
||||
border: 1px solid #C0C0C0;
|
||||
margin-top: 0.5em;
|
||||
margin-left: 0em;
|
||||
}
|
||||
input[type="range"]::-webkit-slider-thumb{
|
||||
-webkit-appearance:none;
|
||||
width: 25px;
|
||||
height:25px;
|
||||
background:#7E4C80;
|
||||
position:relative;
|
||||
z-index:3;
|
||||
}
|
||||
.rangeNumber{
|
||||
width: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
* input[type=text] {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding-inline-start: 5px;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 27px;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
* input[type="checkbox"] {
|
||||
}
|
||||
* input[type=submit] {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 1.3em;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
* button {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 0.5em;
|
||||
margin-bottom: 15px;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 1.3em;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
* textarea {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 0em;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-radius: 0px;
|
||||
font-family: 'Courier', Arial, sans-serif;
|
||||
font-size: 1em;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
.rangeNumber{
|
||||
width: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.fullTextField[type=text] {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding-inline-start: 5px;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 27px;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
|
||||
* input[type=submit] {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 1.3em;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
|
||||
* button {
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding-inline: 5px;
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 1.3em;
|
||||
color: rgb(119, 119, 119);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
textarea {
|
||||
grid-row: 1 / 2;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
|
||||
color: #777;
|
||||
}
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
.grids-class{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 20px);
|
||||
grid-template-rows: repeat(auto-fill, 20px);
|
||||
grid-gap: 0px;
|
||||
}
|
||||
.svg-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.buttondiv-class {
|
||||
flex: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.buttondivmid-class {
|
||||
width: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
#image-container {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
#button-container {
|
||||
display: flex;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.buttonclass {
|
||||
flex: 1;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.gap {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
#submitConvert::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
background-image: url('data:image/svg+xml;utf8, <svg style="width:24px;height:24px" viewBox="0 0 24 24" <path fill="currentColor" d="M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z" /></svg>');
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
#sizeDiv * {
|
||||
display: inline-block;
|
||||
}
|
||||
.sizeInputFields{
|
||||
width: 50px;
|
||||
background-color: #333333;
|
||||
border: 1px solid #C0C0C0;
|
||||
padding-inline-start: 5px;
|
||||
margin-top: 10px;
|
||||
height: 27px;
|
||||
border-radius: 0px;
|
||||
font-family: 'Arcade', Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: rgb(119, 119, 119);
|
||||
}
|
||||
Reference in New Issue
Block a user