Custom Palette Creator
This commit is contained in:
parent
d17a41f7f1
commit
8f5373f034
@ -221,6 +221,7 @@ function writeChunks(srcDir, specs, resultFile) {
|
|||||||
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
|
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
|
||||||
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
|
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
|
||||||
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
|
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
|
||||||
|
writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
|
||||||
/*
|
/*
|
||||||
writeChunks(
|
writeChunks(
|
||||||
"wled00/data",
|
"wled00/data",
|
||||||
|
426
wled00/data/cpal/cpal.htm
Normal file
426
wled00/data/cpal/cpal.htm
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
|
||||||
|
<!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>WLED Pixel Art Converter</title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var d = document;
|
||||||
|
function gId(e) {return d.getElementById(e);}
|
||||||
|
function cE(e) {return d.createElement(e);}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #111;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #ddd;
|
||||||
|
margin: 0 10px;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
line-height: 0.5;
|
||||||
|
}
|
||||||
|
#parent-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bottomContainer {
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gradient-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-marker {
|
||||||
|
position: absolute;
|
||||||
|
height: 30px;
|
||||||
|
width: 7px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: rgb(192, 192, 192);
|
||||||
|
border: 2px solid rgba(68, 68, 68, 0.5);
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.color-picker-marker {
|
||||||
|
position: absolute;
|
||||||
|
height: 7px;
|
||||||
|
width: 7px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: rgb(192, 192, 192);
|
||||||
|
border: 2px solid rgba(68, 68, 68, 0.5);
|
||||||
|
top: 150%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.delete-marker {
|
||||||
|
position: absolute;
|
||||||
|
height: 5px;
|
||||||
|
width: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
border: 3px solid rgb(155, 40, 40);
|
||||||
|
top: 220%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.color-picker{
|
||||||
|
position: absolute;
|
||||||
|
height: 1px;
|
||||||
|
width: 1px;
|
||||||
|
border: 1px;
|
||||||
|
top: 150%;
|
||||||
|
z-index: 1;
|
||||||
|
border-color: #111;
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
.buttonclass {
|
||||||
|
padding: 0,0,0,0px;
|
||||||
|
margin: 0,0,0,0px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
#bottomContainer span {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
#info {
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="display: flex; justify-content: center;">
|
||||||
|
<h1 style="display: flex; align-items: center;">
|
||||||
|
<svg style="width:36px;height:36px;margin-right:6px;" viewBox="0 0 32 32">
|
||||||
|
<rect style="fill:#003FFF" x="6" y="22" width="8" height="4"/>
|
||||||
|
<rect style="fill:#003FFF" x="14" y="14" width="4" height="8"/>
|
||||||
|
<rect style="fill:#003FFF" x="18" y="10" width="4" height="8"/>
|
||||||
|
<rect style="fill:#003FFF" x="22" y="6" width="8" height="4"/>
|
||||||
|
</svg>
|
||||||
|
WLED Custom Palette Creator
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="parent-container">
|
||||||
|
<div id="gradient-box"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: center;">
|
||||||
|
<div id="bottomContainer">
|
||||||
|
<span id="idSpan">
|
||||||
|
<label for="palId">Palette#: </label>
|
||||||
|
<input id="palId" type="number" min="0" max="9" style="background-color: #111; color: #fff; border: none; font-weight: bold; text-align: center; width: 40px;" value="0">
|
||||||
|
</span>
|
||||||
|
<span id="sendSpan" onclick="initiateUpload()"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: center;">
|
||||||
|
<div id="info">
|
||||||
|
Click on the gradient to add new color slider. Click the colored box below the slider to change color. Click the red box below indicator (and confirm) to delete. Select palette number. Click the cloud icon to upload.
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; justify-content: center;"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var gradientBox = gId('gradient-box');
|
||||||
|
|
||||||
|
var pxCol = {};
|
||||||
|
var tCol = {};
|
||||||
|
var rect = gradientBox.getBoundingClientRect();
|
||||||
|
var gradientLength = rect.width;
|
||||||
|
var mOffs = Math.round((gradientLength / 256) / 2) - 5;
|
||||||
|
|
||||||
|
function recOf(){
|
||||||
|
rect = gradientBox.getBoundingClientRect();
|
||||||
|
gradientLength = rect.width;
|
||||||
|
mOffs = Math.round((gradientLength / 256) / 2) - 5;
|
||||||
|
console.log(mOffs)
|
||||||
|
}
|
||||||
|
|
||||||
|
gradientBox.addEventListener("click", clikOnGradient);
|
||||||
|
gId("sendSpan").innerHTML =
|
||||||
|
'<svg class="svg-icon" style="width:36px;height:36px" viewBox="0 0 24 24"> <path id=sendSvgP fill="#fff" 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>';
|
||||||
|
|
||||||
|
|
||||||
|
//Sets start and stop, mandatory
|
||||||
|
addC(0);
|
||||||
|
addC(255);
|
||||||
|
|
||||||
|
updateGradient(); //Sets the gradient at startup
|
||||||
|
|
||||||
|
function clikOnGradient(e){
|
||||||
|
addC(Math.round((e.offsetX/gradientLength)*256));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// Add a new colorMarker
|
||||||
|
function addC(truePos){
|
||||||
|
let position = -1;
|
||||||
|
let iExist = false;
|
||||||
|
const colorMarkers = gradientBox.querySelectorAll('.color-marker');
|
||||||
|
|
||||||
|
colorMarkers.forEach((colorMarker, i) => {
|
||||||
|
if(colorMarkers[i].getAttribute("data-truepos") == truePos){
|
||||||
|
iExist = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (colorMarkers.length > 17){iExist = true}
|
||||||
|
if(iExist){
|
||||||
|
return; // Exit the function early if iExist is true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(truePos > 0 && truePos < 255){
|
||||||
|
//calculate first available > 0
|
||||||
|
|
||||||
|
for (var i = 1; i <= 16 && position < 1; i++) {
|
||||||
|
if(!gId("colorMarker"+i))
|
||||||
|
{
|
||||||
|
position = i;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
position = truePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisColor = `#${Math.floor(Math.random()*16777215).toString(16)}`; // set random color as default
|
||||||
|
|
||||||
|
const colorMarker = cE('span'); // create a marker for the color position
|
||||||
|
colorMarker.className = 'color-marker';
|
||||||
|
colorMarker.id = 'colorMarker' + position.toString();
|
||||||
|
colorMarker.setAttribute("data-truepos", truePos); //Used to always have a true position no matter what screen or percentage we use
|
||||||
|
colorMarker.setAttribute("data-truecol", thisColor); //Used to hold the color of the position in the gradient connected to a true position
|
||||||
|
colorMarker.setAttribute("data-offset", mOffs);
|
||||||
|
colorMarker.addEventListener('click', stopFurtherProp); //Added to prevent the gradient click to fire when covered by a marker
|
||||||
|
colorMarker.style.left = `${Math.round((gradientLength / 256) * truePos)+mOffs}px`;
|
||||||
|
|
||||||
|
const colorPicker = cE('input');
|
||||||
|
colorPicker.type = 'color';
|
||||||
|
colorPicker.value = thisColor;
|
||||||
|
colorPicker.className = 'color-picker';
|
||||||
|
colorPicker.id = 'colorPicker' + position.toString();
|
||||||
|
colorPicker.addEventListener('input', updateGradient);
|
||||||
|
colorPicker.addEventListener('click',cpClk)
|
||||||
|
|
||||||
|
const colorPickerMarker = cE('span'); // create a marker for the color position
|
||||||
|
colorPickerMarker.className = 'color-picker-marker';
|
||||||
|
colorPickerMarker.id = 'colorPickerMarker' + position.toString();
|
||||||
|
colorPickerMarker.addEventListener('click', colClk);
|
||||||
|
colorPickerMarker.style.left = colorMarker.style.left;
|
||||||
|
colorPicker.style.left = colorMarker.style.left;
|
||||||
|
|
||||||
|
const deleteMarker = cE('span'); // create a marker for the color position
|
||||||
|
if(position > 0 && position < 255){
|
||||||
|
deleteMarker.className = 'delete-marker';
|
||||||
|
deleteMarker.id = 'deleteMarker' + position.toString();
|
||||||
|
deleteMarker.addEventListener('click', (e) => {
|
||||||
|
deleteColor(e);
|
||||||
|
});
|
||||||
|
deleteMarker.style.left = colorMarker.style.left
|
||||||
|
}
|
||||||
|
|
||||||
|
colorMarker.style.backgroundColor = colorPicker.value; // set marker color to match color picker
|
||||||
|
colorPickerMarker.style.backgroundColor = colorPicker.value;
|
||||||
|
//colorPicker.style.backgroundColor = colorPicker.value;
|
||||||
|
|
||||||
|
gradientBox.appendChild(colorPicker); // append the color picker to the row div
|
||||||
|
//colorPickerMarker.appendChild(colorPicker);
|
||||||
|
gradientBox.appendChild(colorMarker);
|
||||||
|
gradientBox.appendChild(colorPickerMarker);
|
||||||
|
if(position != 0 && position != 255) {gradientBox.appendChild(deleteMarker)}; // append the marker if not start or end
|
||||||
|
//make markers slidable IF they are not the first or last slider
|
||||||
|
if(position > 0 && position < 255){
|
||||||
|
makeMeDrag(gId(colorMarker.id));
|
||||||
|
}
|
||||||
|
setTooltipMarker(gId(colorMarker.id));
|
||||||
|
|
||||||
|
updateGradient();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// Update Gradient
|
||||||
|
function updateGradient() {
|
||||||
|
const colorMarkers = gradientBox.querySelectorAll('.color-marker');
|
||||||
|
pxCol = {};
|
||||||
|
tCol = {}
|
||||||
|
colorMarkers.forEach((colorMarker, index) => {
|
||||||
|
const thisColorPicker = gId(colorMarkers[index].id.replace('colorMarker', 'colorPicker'));
|
||||||
|
const colorToSet = thisColorPicker.value;
|
||||||
|
//thisColorPicker.style.backgroundColor = colorToSet;
|
||||||
|
gId(colorMarkers[index].id.replace('colorMarker', 'colorPickerMarker')).style.backgroundColor = colorToSet;
|
||||||
|
colorMarkers[index].style.backgroundColor = colorToSet;
|
||||||
|
colorMarkers[index].setAttribute("data-truecol", colorToSet);
|
||||||
|
const tPos = colorMarkers[index].getAttribute("data-truepos");
|
||||||
|
const gradientPos = Math.round((gradientLength / 256)*tPos);
|
||||||
|
pxCol[gradientPos] = colorToSet;
|
||||||
|
tCol[tPos] = colorToSet;
|
||||||
|
});
|
||||||
|
gradientString = 'linear-gradient(to right';
|
||||||
|
Object.entries(pxCol).forEach(([p, c]) => {
|
||||||
|
gradientString += `, ${c} ${p}px`;
|
||||||
|
});
|
||||||
|
gradientString += ')';
|
||||||
|
gradientBox.style.background = gradientString;
|
||||||
|
//gId("jsonstring").innerHTML = calcJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function stopFurtherProp(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function colClk(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
let cp = gId(e.srcElement.id.replace("Marker",""));
|
||||||
|
cp.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cpClk(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
//This neat little function makes any element draggable on the X-axis.
|
||||||
|
//Just call: makeMeDrag(myElement); And you are good to go.
|
||||||
|
function makeMeDrag(elmnt) {
|
||||||
|
var posNew = 0, mousePos = 0, mouseOffset = 0
|
||||||
|
|
||||||
|
//Set these to whatever you want to limit your movement to
|
||||||
|
var rect = gradientBox.getBoundingClientRect();
|
||||||
|
var maxX = rect.right; // maximum X coordinate
|
||||||
|
var minX = rect.left; // minimum X coordinate i.e. also offset from left of screen
|
||||||
|
var gradientLength = maxX - minX + 1;
|
||||||
|
|
||||||
|
elmnt.onmousedown = dragMouseDown;
|
||||||
|
|
||||||
|
function dragMouseDown(e) {
|
||||||
|
e = e || window.event;
|
||||||
|
e.preventDefault();
|
||||||
|
// get the mouse cursor position at startup:
|
||||||
|
mousePos = e.clientX;
|
||||||
|
d.onmouseup = closeDragElement;
|
||||||
|
// call a function whenever the cursor moves:
|
||||||
|
d.onmousemove = elementDrag;
|
||||||
|
}
|
||||||
|
|
||||||
|
function elementDrag(e) {
|
||||||
|
e = e || window.event;
|
||||||
|
e.preventDefault();
|
||||||
|
// calculate the new cursor position:
|
||||||
|
posNew = mousePos - e.clientX;
|
||||||
|
mousePos = e.clientX;
|
||||||
|
mousePosInGradient = mousePos - (minX + 1)
|
||||||
|
|
||||||
|
truePos = Math.round((mousePosInGradient/gradientLength)*256);
|
||||||
|
oldTruePos = elmnt.getAttribute("data-truepos");
|
||||||
|
// set the element's new position if new position within min/max limits:
|
||||||
|
if(truePos > 0 && truePos < 255 && oldTruePos != truePos){
|
||||||
|
if(truePos < 64){thisOffset = 0} else if(truePos > 192){thisOffset = 7} else{thisOffset=3}
|
||||||
|
elmnt.style.left = (Math.round((gradientLength/256)*truePos)+mOffs) + "px";
|
||||||
|
gId(elmnt.id.replace('colorMarker', 'colorPickerMarker')).style.left = elmnt.style.left;
|
||||||
|
gId(elmnt.id.replace('colorMarker', 'deleteMarker')).style.left = elmnt.style.left;
|
||||||
|
gId(elmnt.id.replace('colorMarker', 'colorPicker')).style.left = elmnt.style.left;
|
||||||
|
elmnt.setAttribute("data-truepos", truePos);
|
||||||
|
setTooltipMarker(elmnt);
|
||||||
|
updateGradient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDragElement() {
|
||||||
|
/* stop moving when mouse button is released:*/
|
||||||
|
d.onmouseup = null;
|
||||||
|
d.onmousemove = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTooltipMarker(elmnt){
|
||||||
|
elmnt.setAttribute('title', `${elmnt.getAttribute("data-truepos")} : ${elmnt.getAttribute("data-truecol")}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteColor(e) {
|
||||||
|
var trash = cE("div");
|
||||||
|
thisDeleteMarker = e.srcElement;
|
||||||
|
thisColorMarker = gId(thisDeleteMarker.id.replace("delete", "color"));
|
||||||
|
thisColorPickerMarker = gId(thisDeleteMarker.id.replace("delete", "colorPicker"));
|
||||||
|
thisColorPicker = gId(thisDeleteMarker.id.replace("deleteMarker", "colorPicker"));
|
||||||
|
renderOffsetX = 15 - 5;
|
||||||
|
renderX = e.srcElement.getBoundingClientRect().x - renderOffsetX;
|
||||||
|
renderY = e.srcElement.getBoundingClientRect().y + 13;
|
||||||
|
|
||||||
|
trash.id = "trash";
|
||||||
|
trash.innerHTML = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30px" height="30px"><path style="fill:#880000; stroke: #888888; stroke-width: -2px;stroke-dasharray: 0.1, 8;" d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"/></svg>';
|
||||||
|
trash.style.position = "absolute";
|
||||||
|
trash.style.left = (renderX) + "px";
|
||||||
|
trash.style.top = (renderY) + "px";
|
||||||
|
d.body.appendChild(trash);
|
||||||
|
|
||||||
|
trash.addEventListener("click", function(e) {
|
||||||
|
trash.parentNode.removeChild(trash);
|
||||||
|
thisDeleteMarker.parentNode.removeChild(thisDeleteMarker);
|
||||||
|
thisColorPickerMarker.parentNode.removeChild(thisColorPickerMarker);
|
||||||
|
thisColorMarker.parentNode.removeChild(thisColorMarker);
|
||||||
|
thisColorPicker.parentNode.removeChild(thisColorPicker);
|
||||||
|
updateGradient();
|
||||||
|
});
|
||||||
|
e.stopPropagation();
|
||||||
|
// Add event listener to close the trashcan on outside click
|
||||||
|
d.addEventListener("click", removeTrashcan);
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTrashcan(event) {
|
||||||
|
trash = gId("trash");
|
||||||
|
if (event.target != trash && trash) {
|
||||||
|
trash.parentNode.removeChild(trash);
|
||||||
|
d.removeEventListener("click", removeTrashcan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcJSON(){
|
||||||
|
let rStr = '{"palette":['
|
||||||
|
Object.entries(tCol).forEach(([p, c]) => {
|
||||||
|
if(p > 0){rStr += ','}
|
||||||
|
rStr += `${p},${parseInt(c.slice(1, 3), 16)},${parseInt(c.slice(3, 5), 16)},${parseInt(c.slice(5, 7), 16)}`;
|
||||||
|
});
|
||||||
|
rStr += ']}';
|
||||||
|
return rStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initiateUpload() {
|
||||||
|
const data = calcJSON();
|
||||||
|
const fileName = '/palette' + gId('palId').value + '.json';
|
||||||
|
uploadJSON(data, fileName);
|
||||||
|
console.log('File: ', fileName, 'JSON: ', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadJSON(jsonString, fileName) {
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
var blob = new Blob([jsonString], {type: "application/json"});
|
||||||
|
req.addEventListener('load', function(){console.log(this.responseText, ' - ', this.status)});
|
||||||
|
req.addEventListener('error', function(e){console.log('Error: ', e); console.log(' Status: ', this.status);});
|
||||||
|
req.open("POST", "/upload");
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append("data", blob, fileName);
|
||||||
|
req.send(formData);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user