feat: New collection and view

- Add art collection
- Move views to art
- Add new view
This commit is contained in:
sthag 2026-04-20 20:13:58 +02:00
parent 5cd55ab2bc
commit 0f92174143
4 changed files with 199 additions and 2 deletions

View file

@ -0,0 +1,69 @@
---
title: 10print
tags:
- demoArt
---
{% layout 'hippie/simple.liquid' %}
{% block style %}
<style>
canvas {
display: block;
}
</style>
{% endblock %}
{% block body %}
<canvas id="pattern"></canvas>
{% endblock %}
{% block script %}
<script>
const canvas = document.getElementById('pattern');
const ctx = canvas.getContext('2d');
const lineWidth = 20;
const randomSeed = Math.random;
// Function to draw the 10PRINT pattern
function draw10Print() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Set line style
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
// Loop through the canvas grid
for (let x = 0; x < canvas.width; x += lineWidth) {
for (let y = 0; y < canvas.height; y += lineWidth) {
// Randomly choose between two diagonal lines
if (randomSeed() > 0.5) {
// Draw line from top-left to bottom-right
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + lineWidth, y + lineWidth);
ctx.stroke();
} else {
// Draw line from top-right to bottom-left
ctx.beginPath();
ctx.moveTo(x + lineWidth, y);
ctx.lineTo(x, y + lineWidth);
ctx.stroke();
}
}
}
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
draw10Print();
// window.addEventListener('resize', resizeCanvas);
canvas.addEventListener('click', draw10Print);
</script>
{% endblock %}

View file

@ -0,0 +1,167 @@
---
title: Matrix
tags:
- demoArt
---
{% layout 'hippie/simple.liquid' %}
{% block style %}
<style>
html, body {
height: 100vh;
box-sizing: border-box;
}
body {
margin: 0;
background-color: grey;
font-family: 'Courier New', Courier, monospace;
}
#canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
{% endblock %}
{% block body %}
<canvas id="canvas"></canvas>
{% endblock %}
{% block script %}
<script>
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// const observer = new ResizeObserver(() => {
// canvas.width = canvas.clientWidth;
// canvas.height = canvas.clientHeight;
// console.log("resize");
// });
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
// observer.observe(canvas);
ctx.font = "24px 'Courier New', Courier, monospace";
ctx.textBaseline = "top";
let char = characters.charAt(Math.floor(Math.random() * characters.length));
let charMeasure = ctx.measureText(char);
let charW = Math.ceil(Math.max(charMeasure.actualBoundingBoxLeft + charMeasure.actualBoundingBoxRight, charMeasure.width));
let charH = charMeasure.fontBoundingBoxDescent + charMeasure.fontBoundingBoxAscent;
let glyph = {
text: char,
x: 0,
y: 0,
w: charH,
h: charH
}
let lane = [];
let max = Math.floor(canvas.clientHeight / glyph.h);
let newId = undefined;
let holdId = undefined;
let cleanId = undefined;
let newInterval = 300;
let holdInterval = 1000;
let cleanInterval = 400;
let newIndex = 0;
let cleanIndex = randomIntFrom(1, max);
console.log("init", newInterval);
console.log(glyph);
newId = setInterval(newTrail, newInterval);
function newTrail() {
clearInterval(holdId);
glyph.text = characters.charAt(Math.floor(Math.random() * characters.length));
console.log(newIndex, glyph.text);
lane.push([newIndex, glyph.text, glyph.y]);
ctx.fillStyle = "white";
ctx.fillRect(glyph.x, glyph.y, glyph.w, glyph.h);
ctx.fillStyle = "black";
ctx.fillText(glyph.text, Math.floor((charH - charW) / 2), glyph.y);
if (newIndex > 0) {
let prevY = glyph.y - glyph.h;
let alpha = randomBetween(.2, 1);
ctx.clearRect(glyph.x, prevY, glyph.w, glyph.h);
ctx.fillStyle = "rgba(0, 0, 0, " + alpha + ")";
ctx.fillRect(glyph.x, prevY, glyph.w, glyph.h);
ctx.fillStyle = "rgba(255, 255, 255, " + alpha + ")";
// TODO: can not use array lane if it is changed by cleanTrail()
ctx.fillText(lane[newIndex - 1][1], Math.floor((charH - charW) / 2), prevY);
}
if (newIndex === cleanIndex) {
console.log("clean", cleanInterval, cleanIndex);
cleanId = setInterval(cleanTrail, cleanInterval);
}
newIndex++;
glyph.y += glyph.h;
if (newIndex > max) {
// console.log("hold", holdInterval);
console.log("end");
clearInterval(newId);
newIndex = 0;
glyph.y = 0;
newInterval = randomIntFrom(100, 1000, 2);
// holdId = setTimeout(() => {
// console.log("clean", cleanInterval);
// holdInterval = randomIntFrom(1000, 10000, 3);
// cleanId = setInterval(cleanTrail, cleanInterval);
// }, holdInterval);
}
}
function cleanTrail() {
let pos = lane.shift();
console.log(pos);
ctx.clearRect(0, pos[2], glyph.w, glyph.h);
if (!lane.length) {
console.log("hold", holdInterval);
clearInterval(cleanId);
holdId = setTimeout(() => {
console.clear();
console.log("new", newInterval);
cleanIndex = randomIntFrom(1, max);
cleanInterval = randomIntFrom(1000, 10000, 3);
holdInterval = randomIntFrom(1000, 10000, 3);
newId = setInterval(newTrail, newInterval);
}, holdInterval);
}
}
function randomBetween(min, max) {
return (Math.random() * (max - min) + min).toFixed(2);
}
function randomIntFrom(min, max, pos = 0) {
pos = Math.pow(10, pos);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor((Math.random() * (max - min + 1) + min) / pos) * pos;
}
</script>
{% endblock %}

View file

@ -0,0 +1,186 @@
---
title: Squares²
tags:
- demoArt
---
{% layout 'hippie/simple.liquid' %}
{% block style %}
<style>
canvas {
display: block;
}
</style>
{% endblock %}
{% block body %}{% endblock %}
{% block script %}
<script>
function fillCanvasWithSquares(maxSquareSize = 128, minSquareSize = 8, largeSquarePercentage = 40) {
const canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const colors = [
'#fad803', '#d30a51', '#273f8b',
'#b7e0f0', '#52bed1', '#0c85ff'
];
// Verify maxSquareSize and minSquareSize are powers of 2
if (!isPowerOfTwo(maxSquareSize) || !isPowerOfTwo(minSquareSize)) {
console.error('Both maxSquareSize and minSquareSize must be powers of 2');
return;
}
if (minSquareSize > maxSquareSize) {
console.error('minSquareSize must be <= maxSquareSize');
return;
}
if (largeSquarePercentage < 0 || largeSquarePercentage > 100) {
console.error('largeSquarePercentage must be between 0 and 100');
return;
}
function isPowerOfTwo(n) {
return n > 0 && (n & (n - 1)) === 0;
}
// Grid to track occupied cells
let grid = [];
function initializeGrid() {
grid = Array(Math.ceil(canvas.height / minSquareSize))
.fill(null)
.map(() => Array(Math.ceil(canvas.width / minSquareSize)).fill(false));
}
function canPlaceSquare(gridX, gridY, sizeInCells) {
if (gridY + sizeInCells > grid.length || gridX + sizeInCells > grid[0].length) {
return false;
}
for (let y = gridY; y < gridY + sizeInCells; y++) {
for (let x = gridX; x < gridX + sizeInCells; x++) {
if (grid[y][x]) return false;
}
}
return true;
}
function markSquareOccupied(gridX, gridY, sizeInCells) {
for (let y = gridY; y < gridY + sizeInCells; y++) {
for (let x = gridX; x < gridX + sizeInCells; x++) {
grid[y][x] = true;
}
}
}
function drawSquare(gridX, gridY, sizeInPixels) {
const pixelX = gridX * minSquareSize;
const pixelY = gridY * minSquareSize;
const color = colors[Math.floor(Math.random() * colors.length)];
ctx.fillStyle = color;
ctx.fillRect(pixelX, pixelY, sizeInPixels, sizeInPixels);
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
ctx.strokeRect(pixelX, pixelY, sizeInPixels, sizeInPixels);
}
function getSizesInDescendingOrder() {
const sizes = [];
for (let size = maxSquareSize; size >= minSquareSize; size /= 2) {
sizes.push(size);
}
return sizes;
}
function countPossibleSquares(size) {
const sizeInCells = size / minSquareSize;
let count = 0;
for (let gridY = 0; gridY < grid.length; gridY++) {
for (let gridX = 0; gridX < grid[0].length; gridX++) {
if (canPlaceSquare(gridX, gridY, sizeInCells)) {
count++;
}
}
}
return count;
}
function fillCanvasWithPercentage() {
const sizes = getSizesInDescendingOrder();
// Fill the largest squares based on percentage
const largestSize = sizes[0];
const maxPossibleLargeSquares = countPossibleSquares(largestSize);
const targetLargeSquares = Math.floor((maxPossibleLargeSquares * largeSquarePercentage) / 100);
const largestSizeInCells = largestSize / minSquareSize;
// Collect all valid positions for largest squares
if (targetLargeSquares > 0) {
const positions = [];
for (let gridY = 0; gridY < grid.length; gridY++) {
for (let gridX = 0; gridX < grid[0].length; gridX++) {
if (canPlaceSquare(gridX, gridY, largestSizeInCells)) {
positions.push({gridX, gridY});
}
}
}
// Shuffle positions array
positions.sort(() => Math.random() - 0.5);
// Place squares up to target percentage
let placedCount = 0;
for (let i = 0; i < positions.length && placedCount < targetLargeSquares; i++) {
const {gridX, gridY} = positions[i];
// Double-check the position is still valid before placing
if (canPlaceSquare(gridX, gridY, largestSizeInCells)) {
markSquareOccupied(gridX, gridY, largestSizeInCells);
drawSquare(gridX, gridY, largestSize);
placedCount++;
}
}
}
// Fill remaining space with all sizes in descending order
// Skip the largest size since we already handled it
for (let i = 1; i < sizes.length; i++) {
const size = sizes[i];
const sizeInCells = size / minSquareSize;
for (let gridY = 0; gridY < grid.length; gridY++) {
for (let gridX = 0; gridX < grid[0].length; gridX++) {
if (canPlaceSquare(gridX, gridY, sizeInCells)) {
markSquareOccupied(gridX, gridY, sizeInCells);
drawSquare(gridX, gridY, size);
}
}
}
}
}
initializeGrid();
fillCanvasWithPercentage();
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
initializeGrid();
fillCanvasWithPercentage();
});
}
fillCanvasWithSquares(128, 8, 40);
</script>
{% endblock %}