feat: New collection and view
- Add art collection - Move views to art - Add new view
This commit is contained in:
parent
5cd55ab2bc
commit
0f92174143
4 changed files with 199 additions and 2 deletions
69
source/view/demo/art/10print.liquid
Normal file
69
source/view/demo/art/10print.liquid
Normal 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 %}
|
||||
167
source/view/demo/art/matrix.liquid
Normal file
167
source/view/demo/art/matrix.liquid
Normal 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 %}
|
||||
186
source/view/demo/art/squares.liquid
Normal file
186
source/view/demo/art/squares.liquid
Normal 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 %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue