Skip to content

Instantly share code, notes, and snippets.

@texodus
Last active August 17, 2020 03:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save texodus/4c6537e23dff3c8f97c316559cef012e to your computer and use it in GitHub Desktop.
Save texodus/4c6537e23dff3c8f97c316559cef012e to your computer and use it in GitHub Desktop.
regular-table / Canvas Data Model
license: apache-2.0
height: 800

Canvas Data Model

An example of using a <canvas> element as a data model for regular-table. As you mouseover the image, a cursor tooltip <regular-table> shows a zoom-in via a virtual data model which defers to context.getImageData().

<script src="https://cdn.jsdelivr.net/npm/regular-table@0.0.7/dist/umd/regular-table.js"></script>
<link rel='stylesheet' href="https://cdn.jsdelivr.net/npm/regular-table@0.0.7/dist/css/material.css">
body {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0;
    cursor: none;
}
#scroll_container {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0;
    overflow: auto;
}
regular-table tbody th {
    text-align: center;
}
regular-table tbody th:last-of-type,
regular-table thead tr:first-child th
{
    border-right: 1px solid white;
}
regular-table thead tr:first-child th {
    text-align: left;
    padding-left: 6px;
}
regular-table thead tr:first-child th:first-child,
regular-table thead tr:nth-child(2) th:nth-child(2) {
    border-right: 1px solid white;
}
regular-table th:not(:first-child),
regular-table td {
    height: 20px !important;
    min-width: 20px !important;
    max-width: 20px !important;
    padding: 0px;
}
regular-table th {           
    color: white;
    font-family: monospace;
}
tbody th[rowspan] {
    vertical-align: top;
    border-bottom: 1px solid white;
    /* border-right: 0px !important; */
}
regular-table {
    position: absolute;
    top: 0;
    left: 0;
    width: 400px;
    height: 400px;
    pointer-events: none;
}
regular-table > div {
    padding: 0px;
    border: 1px solid white;
    border-top-width: 0;
    border-left-width: 0;
}
regular-table::-webkit-scrollbar-thumb {
    background-color: #fff !important;
}
#reticle {
    position: absolute;
    width: 20px;
    height: 20px;
    border: 1px solid white;
}

Alternative large format images

<div id="scroll_container">
    <div id="reticle"></div>
    <regular-table></regular-table>
    <img id="ref_image" crossorigin="anonymous" src="https://upload.wikimedia.org/wikipedia/commons/7/7a/Cassius_Marcellus_Coolidge_-_Poker_Game_%281894%29.png"></img>
</div>
const ref_image = document.getElementById("ref_image");
const table = document.getElementsByTagName("regular-table")[0];
const canvas = document.createElement("canvas");
const scroll_container = window.scroll_container;
const reticle = window.reticle;

const formatter = new Intl.NumberFormat("en-us");
const clamp = (x, y) => formatter.format(Math.floor(x / y) * y);

ref_image.onload = async function () {
    canvas.width = ref_image.width;
    canvas.height = ref_image.height;
    const context = canvas.getContext("2d");
    context.drawImage(ref_image, 0, 0, ref_image.width, ref_image.height);
    scroll_container.removeChild(ref_image);
    scroll_container.appendChild(canvas);

    table.addStyleListener(() => {
        const tds = table.querySelectorAll("td");
        for (const td of tds) {
            td.style.backgroundColor = td.textContent;
            td.innerHTML = " ";
        }
    });

    const column_names = Array.from(Array(canvas.width).keys());

    table.setDataListener((x0, y0, x1, y1) => {
        const data = [];
        for (let i = x0; i < x1; i++) {
            const column = [];
            data.push(column);
            for (let j = y0; j < y1; j++) {
                const [r, g, b] = context.getImageData(i, j, 1, 1).data;
                column.push(`rgb(${r},${g},${b})`);
            }
        }
        return {
            data,
            row_headers: y1 - y0 === 0 ? [] : Array.from(Array(Math.floor(y1 - y0)).keys()).map((z) => [clamp(y0 + z, 10), (y0 + z) % 10]),
            column_headers: column_names.slice(x0, x1).map((x) => [clamp(x, 10), x % 10]),
            num_rows: canvas.height,
            num_columns: column_names.length,
        };
    });

    await table.draw();
};

if (ref_image.complete || ref_image.naturalWidth > 0) {
    ref_image.onload();
}

window.addEventListener("mousemove", (event) => {
    const x = event.clientX + scroll_container.scrollLeft;
    const y = event.clientY + scroll_container.scrollTop;
    reticle.style.top = `${y}px`;
    reticle.style.left = `${x}px`;
    const top_scroll_limit = scroll_container.scrollTop + window.innerHeight - 424;
    const left_scroll_limit = scroll_container.scrollLeft + window.innerWidth - 424;
    if (top_scroll_limit < y + 20 && left_scroll_limit < x + 20) {
        if (y - top_scroll_limit < x - left_scroll_limit) {
            table.style.top = `${top_scroll_limit}px`;
            table.style.left = `${x - 424}px`;
        } else {
            table.style.top = `${y - 424}px`;
            table.style.left = `${left_scroll_limit}px`;
        }
    } else {
        table.style.top = `${Math.min(top_scroll_limit, y + 20)}px`;
        table.style.left = `${Math.min(left_scroll_limit, x + 20)}px`;
    }
    table.scrollToCell(x, y, canvas.width, canvas.height);
});
body {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
cursor: none;
}
#scroll_container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
overflow: auto;
}
regular-table tbody th {
text-align: center;
}
regular-table tbody th:last-of-type,
regular-table thead tr:first-child th
{
border-right: 1px solid white;
}
regular-table thead tr:first-child th {
text-align: left;
padding-left: 6px;
}
regular-table thead tr:first-child th:first-child,
regular-table thead tr:nth-child(2) th:nth-child(2) {
border-right: 1px solid white;
}
regular-table th:not(:first-child),
regular-table td {
height: 20px !important;
min-width: 20px !important;
max-width: 20px !important;
padding: 0px;
}
regular-table th {
color: white;
font-family: monospace;
}
tbody th[rowspan] {
vertical-align: top;
border-bottom: 1px solid white;
/* border-right: 0px !important; */
}
regular-table {
position: absolute;
top: 0;
left: 0;
width: 400px;
height: 400px;
pointer-events: none;
}
regular-table > div {
padding: 0px;
border: 1px solid white;
border-top-width: 0;
border-left-width: 0;
}
regular-table::-webkit-scrollbar-thumb {
background-color: #fff !important;
}
#reticle {
position: absolute;
width: 20px;
height: 20px;
border: 1px solid white;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/regular-table@0.0.7/dist/umd/regular-table.js"></script>
<link rel='stylesheet' href="https://cdn.jsdelivr.net/npm/regular-table@0.0.7/dist/css/material.css">
<div id="scroll_container">
<div id="reticle"></div>
<regular-table></regular-table>
<img id="ref_image" crossorigin="anonymous" src="https://upload.wikimedia.org/wikipedia/commons/7/7a/Cassius_Marcellus_Coolidge_-_Poker_Game_%281894%29.png"></img>
</div>
<link rel="stylesheet" href="index.css">
<script src="index.js"></script>
</body>
</html>
const ref_image = document.getElementById("ref_image");
const table = document.getElementsByTagName("regular-table")[0];
const canvas = document.createElement("canvas");
const scroll_container = window.scroll_container;
const reticle = window.reticle;
const formatter = new Intl.NumberFormat("en-us");
const clamp = (x, y) => formatter.format(Math.floor(x / y) * y);
ref_image.onload = async function () {
canvas.width = ref_image.width;
canvas.height = ref_image.height;
const context = canvas.getContext("2d");
context.drawImage(ref_image, 0, 0, ref_image.width, ref_image.height);
scroll_container.removeChild(ref_image);
scroll_container.appendChild(canvas);
table.addStyleListener(() => {
const tds = table.querySelectorAll("td");
for (const td of tds) {
td.style.backgroundColor = td.textContent;
td.innerHTML = " ";
}
});
const column_names = Array.from(Array(canvas.width).keys());
table.setDataListener((x0, y0, x1, y1) => {
const data = [];
for (let i = x0; i < x1; i++) {
const column = [];
data.push(column);
for (let j = y0; j < y1; j++) {
const [r, g, b] = context.getImageData(i, j, 1, 1).data;
column.push(`rgb(${r},${g},${b})`);
}
}
return {
data,
row_headers: y1 - y0 === 0 ? [] : Array.from(Array(Math.floor(y1 - y0)).keys()).map((z) => [clamp(y0 + z, 10), (y0 + z) % 10]),
column_headers: column_names.slice(x0, x1).map((x) => [clamp(x, 10), x % 10]),
num_rows: canvas.height,
num_columns: column_names.length,
};
});
await table.draw();
};
if (ref_image.complete || ref_image.naturalWidth > 0) {
ref_image.onload();
}
window.addEventListener("mousemove", (event) => {
const x = event.clientX + scroll_container.scrollLeft;
const y = event.clientY + scroll_container.scrollTop;
reticle.style.top = `${y}px`;
reticle.style.left = `${x}px`;
const top_scroll_limit = scroll_container.scrollTop + window.innerHeight - 424;
const left_scroll_limit = scroll_container.scrollLeft + window.innerWidth - 424;
if (top_scroll_limit < y + 20 && left_scroll_limit < x + 20) {
if (y - top_scroll_limit < x - left_scroll_limit) {
table.style.top = `${top_scroll_limit}px`;
table.style.left = `${x - 424}px`;
} else {
table.style.top = `${y - 424}px`;
table.style.left = `${left_scroll_limit}px`;
}
} else {
table.style.top = `${Math.min(top_scroll_limit, y + 20)}px`;
table.style.left = `${Math.min(left_scroll_limit, x + 20)}px`;
}
table.scrollToCell(x, y, canvas.width, canvas.height);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment