new components and stamps
BIN
nginx/vue/public/img/stamps/3ds.jpg
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
nginx/vue/public/img/stamps/ai.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
nginx/vue/public/img/stamps/demo.gif
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
nginx/vue/public/img/stamps/fry.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
nginx/vue/public/img/stamps/haha.gif
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
nginx/vue/public/img/stamps/lain.gif
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
nginx/vue/public/img/stamps/rei.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
nginx/vue/public/img/stamps/teto.webp
Normal file
|
After Width: | Height: | Size: 536 KiB |
BIN
nginx/vue/public/img/stamps/tetris.gif
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
nginx/vue/public/img/stamps/tf2.gif
Normal file
|
After Width: | Height: | Size: 30 KiB |
@@ -205,28 +205,28 @@ td {
|
|||||||
@media (max-width: 850px) {
|
@media (max-width: 850px) {
|
||||||
.a4page-portrait {
|
.a4page-portrait {
|
||||||
width: 100%; /* fill mobile width */
|
width: 100%; /* fill mobile width */
|
||||||
height: auto; /* adjust height automatically */
|
height: fit-content;
|
||||||
margin: 0 auto; /* center horizontally */
|
margin: 0 auto; /* center horizontally */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.a4page-landscape {
|
.a4page-landscape {
|
||||||
width: 100%; /* fill mobile width */
|
width: 100%; /* fill mobile width */
|
||||||
height: auto; /* adjust height automatically */
|
height: fit-content;
|
||||||
margin: 0 auto; /* center horizontally */
|
margin: 0 auto; /* center horizontally */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.a5page-portrait {
|
.a5page-portrait {
|
||||||
width: 100%; /* fill mobile width */
|
width: 100%;
|
||||||
height: auto; /* adjust height automatically */
|
height: fit-content;
|
||||||
margin: 0 auto; /* center horizontally */
|
margin: 0 auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.a5page-landscape {
|
.a5page-landscape {
|
||||||
width: 100%; /* fill mobile width */
|
width: 100%;
|
||||||
height: auto; /* adjust height automatically */
|
height: fit-content;
|
||||||
margin: 0 auto; /* center horizontally */
|
margin: 0 auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,29 @@ import OptionalLinkTable from "@/components/util/OptionalLinkTable.vue";
|
|||||||
import Header from "@/components/text/Header.vue";
|
import Header from "@/components/text/Header.vue";
|
||||||
|
|
||||||
const data = [
|
const data = [
|
||||||
|
{
|
||||||
|
type: "Anime",
|
||||||
|
name: "Cowboy bebop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Game",
|
||||||
|
name: "Rounds",
|
||||||
|
link: "https://store.steampowered.com/app/1557740/ROUNDS/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Boardgame",
|
||||||
|
name: "Root",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Game",
|
||||||
|
name: "The farmer was replaced",
|
||||||
|
link: "https://store.steampowered.com/app/2060160/The_Farmer_Was_Replaced/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Game",
|
||||||
|
name: "Higurashi When They Cry Hou - Ch.1",
|
||||||
|
link: "https://store.steampowered.com/app/310360/Higurashi_When_They_Cry_Hou__Ch1_Onikakushi/",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "Substack",
|
type: "Substack",
|
||||||
name: "By Ellie",
|
name: "By Ellie",
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ const gym = [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col place-content-between items-center">
|
||||||
<Header>Gym</Header>
|
<Header>Gym</Header>
|
||||||
<p class="m-3">I'm not a gym geek but I always do:</p>
|
<p>I'm not a gym geek</p>
|
||||||
<div class="overflow-scroll w-full flex-1 border-box">
|
<p>4/7 days I do:</p>
|
||||||
|
<div class="overflow-scroll w-full border-box">
|
||||||
<OptionalLinkTable class="w-full" :data="gym" />
|
<OptionalLinkTable class="w-full" :data="gym" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
import Touchscreen from "@/components/util/Touchscreen.vue";
|
||||||
|
import { shuffleArray } from "@/js/utils.js";
|
||||||
|
|
||||||
|
let srcs = [
|
||||||
|
"/img/stamps/portal.gif",
|
||||||
|
"/img/stamps/miku.gif",
|
||||||
|
"/img/stamps/utau.gif",
|
||||||
|
"/img/stamps/teto.webp",
|
||||||
|
"/img/stamps/3ds.jpg",
|
||||||
|
"/img/stamps/fry.png",
|
||||||
|
"/img/stamps/ai.png",
|
||||||
|
"/img/stamps/rei.png",
|
||||||
|
"/img/stamps/tetris.gif",
|
||||||
|
"/img/stamps/tf2.gif",
|
||||||
|
"/img/stamps/demo.gif",
|
||||||
|
];
|
||||||
|
shuffleArray(srcs);
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="grid grid-cols-4 overflow-scroll gap-0">
|
<Touchscreen>
|
||||||
|
<div class="flex flex-wrap tst">
|
||||||
<a href="https://www.adam-french.co.uk">
|
<a href="https://www.adam-french.co.uk">
|
||||||
<img src="https://www.adam-french.co.uk/img/stamps/mine.gif" />
|
<img src="https://www.adam-french.co.uk/img/stamps/mine.gif" />
|
||||||
</a>
|
</a>
|
||||||
@@ -9,10 +32,9 @@
|
|||||||
alt="jacobbarron.xyz"
|
alt="jacobbarron.xyz"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<img src="/img/stamps/portal.gif" />
|
<img v-for="src in srcs" :src="src" />
|
||||||
<img src="/img/stamps/miku.gif" />
|
|
||||||
<img src="/img/stamps/utau.gif" />
|
|
||||||
</div>
|
</div>
|
||||||
|
</Touchscreen>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -20,4 +42,7 @@ img {
|
|||||||
width: 89px;
|
width: 89px;
|
||||||
height: 59px;
|
height: 59px;
|
||||||
}
|
}
|
||||||
|
.tst {
|
||||||
|
width: calc(89px * 4);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="container" class="overflow-y-auto">
|
<div ref="container" @mouseover="handleHover" class="overflow-y-auto">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -14,38 +14,37 @@ const PAUSE = 2000; // ms at top/bottom
|
|||||||
const FRAME_TIME = 50; // ms at top/bottom
|
const FRAME_TIME = 50; // ms at top/bottom
|
||||||
|
|
||||||
let direction = 1; // 1 = down, -1 = up
|
let direction = 1; // 1 = down, -1 = up
|
||||||
let paused = false;
|
|
||||||
let rafId;
|
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
|
|
||||||
|
function handleHover() {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(tick, PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
const el = container.value;
|
const el = container.value;
|
||||||
|
|
||||||
el.scrollTop += SPEED * direction;
|
el.scrollTop += SPEED * direction;
|
||||||
|
|
||||||
const reachedBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
|
const reachedBottom = el.scrollTop + el.clientHeight >= el.scrollHeight;
|
||||||
const reachedTop = el.scrollTop <= 0;
|
const reachedTop = el.scrollTop <= 0;
|
||||||
|
|
||||||
if (reachedBottom || reachedTop) {
|
if (reachedBottom || reachedTop) {
|
||||||
direction *= -1;
|
direction *= -1;
|
||||||
|
|
||||||
timeoutId = setTimeout(() => {
|
timeoutId = setTimeout(tick, PAUSE);
|
||||||
paused = false;
|
|
||||||
rafId = setTimeout(tick, FRAME_TIME);
|
|
||||||
}, PAUSE);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rafId = setTimeout(tick, FRAME_TIME);
|
timeoutId = setTimeout(tick, FRAME_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
rafId = setTimeout(tick, FRAME_TIME);
|
timeoutId = setTimeout(tick, FRAME_TIME);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearTimeout(rafId);
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ function playFinishedSound() {
|
|||||||
>
|
>
|
||||||
<h2 class="items-center">Timer</h2>
|
<h2 class="items-center">Timer</h2>
|
||||||
<div v-if="finished && paused" class="flex flex-col">
|
<div v-if="finished && paused" class="flex flex-col">
|
||||||
<div class="flex flex-row p-2">
|
<div class="flex flex-row p-2 place-content-around">
|
||||||
<input
|
<input
|
||||||
class="w-full"
|
class="w-2/3"
|
||||||
v-model="minutesInput"
|
v-model="minutesInput"
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
@@ -77,9 +77,9 @@ function playFinishedSound() {
|
|||||||
/>
|
/>
|
||||||
<p>{{ minutesInput }}m</p>
|
<p>{{ minutesInput }}m</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row p-2">
|
<div class="flex flex-row p-2 place-content-around">
|
||||||
<input
|
<input
|
||||||
class="w-full"
|
class="w-2/3"
|
||||||
v-model="secondsInput"
|
v-model="secondsInput"
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
|
|||||||
67
nginx/vue/src/components/util/Touchscreen.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="container"
|
||||||
|
class="overflow-auto w-full h-full"
|
||||||
|
@mousedown="handleMouseDown"
|
||||||
|
@mousemove="handleMouseMove"
|
||||||
|
@mouseup="handleMouseUp"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
:style="{ cursor: isDragging ? 'grabbing' : 'grab' }"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const container = ref(null);
|
||||||
|
const isDragging = ref(false);
|
||||||
|
const startX = ref(0);
|
||||||
|
const startY = ref(0);
|
||||||
|
const scrollLeft = ref(0);
|
||||||
|
const scrollTop = ref(0);
|
||||||
|
|
||||||
|
const handleMouseDown = (e) => {
|
||||||
|
isDragging.value = true;
|
||||||
|
startX.value = e.pageX - container.value.offsetLeft;
|
||||||
|
startY.value = e.pageY - container.value.offsetTop;
|
||||||
|
scrollLeft.value = container.value.scrollLeft;
|
||||||
|
scrollTop.value = container.value.scrollTop;
|
||||||
|
|
||||||
|
// Prevent text selection while dragging
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseMove = (e) => {
|
||||||
|
if (!isDragging.value) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const x = e.pageX - container.value.offsetLeft;
|
||||||
|
const y = e.pageY - container.value.offsetTop;
|
||||||
|
const walkX = (x - startX.value) * 1; // Multiply by scroll speed factor
|
||||||
|
const walkY = (y - startY.value) * 1;
|
||||||
|
|
||||||
|
container.value.scrollLeft = scrollLeft.value - walkX;
|
||||||
|
container.value.scrollTop = scrollTop.value - walkY;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
isDragging.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
isDragging.value = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Prevent text selection while dragging */
|
||||||
|
div[style*="cursor: grabbing"] {
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
8
nginx/vue/src/js/utils.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export function shuffleArray(array) {
|
||||||
|
for (var i = array.length - 1; i > 0; i--) {
|
||||||
|
var j = Math.floor(Math.random() * (i + 1));
|
||||||
|
var temp = array[i];
|
||||||
|
array[i] = array[j];
|
||||||
|
array[j] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,8 @@ import Consumption from "@/components/home/Consumption.vue";
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="halftone justify-center flex flex-row w-full h-full p-10">
|
<main class="halftone justify-center flex flex-row w-full h-full p-10">
|
||||||
<div class="page a4page-portrait bdr-1 homeGrid relative">
|
<div class="a4height flex flex-row">
|
||||||
|
<div class="a4page-portrait bdr-1 homeGrid relative">
|
||||||
<Intro class="intro" />
|
<Intro class="intro" />
|
||||||
<Listening class="listening" />
|
<Listening class="listening" />
|
||||||
<Stamps class="stamps" />
|
<Stamps class="stamps" />
|
||||||
@@ -29,7 +30,7 @@ import Consumption from "@/components/home/Consumption.vue";
|
|||||||
<Gym class="gym" />
|
<Gym class="gym" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col w-1/12 sidebar border-primary place-content-between h-full sticky m-10"
|
class="flex flex-col sidebar border-primary place-content-between h-full sticky m-10 w-60"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<Time />
|
<Time />
|
||||||
@@ -37,12 +38,19 @@ import Consumption from "@/components/home/Consumption.vue";
|
|||||||
<!-- <Chat class="bdr-2 bg-bg_primary" /> -->
|
<!-- <Chat class="bdr-2 bg-bg_primary" /> -->
|
||||||
<!-- <MusicPlayer /> -->
|
<!-- <MusicPlayer /> -->
|
||||||
</div>
|
</div>
|
||||||
<img src="/img/memes/fire-woman.gif" class="w-80" />
|
<img
|
||||||
|
src="/img/memes/fire-woman.gif"
|
||||||
|
class="border-tertiary border"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.a4height {
|
||||||
|
height: 297mm;
|
||||||
|
}
|
||||||
.homeGrid > * {
|
.homeGrid > * {
|
||||||
@apply border-2 border-dotted;
|
@apply border-2 border-dotted;
|
||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
@@ -56,12 +64,15 @@ import Consumption from "@/components/home/Consumption.vue";
|
|||||||
grid-template-rows: repeat(10, 1fr);
|
grid-template-rows: repeat(10, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1000px) {
|
@media (max-width: 800px) {
|
||||||
.homeGrid {
|
.homeGrid {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
.tr,
|
.tr,
|
||||||
.br,
|
.br,
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
|||||||