diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-12-17 21:33:06 +0100 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-12-17 21:33:06 +0100 |
| commit | 9c37e114b20e6b2a2d4a34fe1f0e44c5ed3439ed (patch) | |
| tree | 42f05bd826fb1e55e70a5402167521253bc8be86 /static | |
| parent | 99701b8d6fc0671fabc2e5d3ab7d6841bff9021a (diff) | |
shuffle: first version
Diffstat (limited to 'static')
| -rw-r--r-- | static/index.js | 3 | ||||
| -rw-r--r-- | static/pages/shuffle/index.css | 42 | ||||
| -rw-r--r-- | static/pages/shuffle/index.js | 106 | ||||
| -rw-r--r-- | static/widgets/image/index.css | 2 | ||||
| -rw-r--r-- | static/widgets/image/index.js | 14 | ||||
| -rw-r--r-- | static/widgets/index.js | 12 |
6 files changed, 158 insertions, 21 deletions
diff --git a/static/index.js b/static/index.js index 0dbb2db..5979d86 100644 --- a/static/index.js +++ b/static/index.js @@ -30,6 +30,7 @@ const reload = () => { } image_viewer.preload_all(); + shuffle.images = images; }); } @@ -76,6 +77,7 @@ const shuffle = ShuffleView.new({ }); const main = MainView.new({ active_view: image_viewer, active_kind: MainView.Kind.upload, + onsearch: () => { main.active_kind = MainView.Kind.upload; main.active_view = image_viewer; @@ -105,6 +107,7 @@ const main = MainView.new({ uploader.send(); }, onshuffle: () => { + shuffle.open(); main.active_kind = MainView.Kind.home; main.active_view = shuffle; }, diff --git a/static/pages/shuffle/index.css b/static/pages/shuffle/index.css index 8c9434e..30d30ac 100644 --- a/static/pages/shuffle/index.css +++ b/static/pages/shuffle/index.css @@ -1,11 +1,39 @@ +:host { + display: block; + width: 100vw; + height: 100vh; + top: 0px; + left: 0px; + overflow: hidden; + position: relative; +} -#container { +sfw-image { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, calc(-50% - 50px)); width: 100%; - height: 100%; - background: var(--page-background); - text-align: center; - align-content: center; - color: #838b97; - font-family: 'Pacifico'; + max-width: calc(100vw - 20px); + max-height: calc(100vh - 200px); user-select: none; + transition: transform 0.05s ease; +} + +#front { + z-index: 1000; +} + +#back { + z-index: 500; + filter: blur(50px); +} + +#date { + margin: 10px; + border-radius: var(--border-radius); + padding: 10px; + background: #efefef99; + backdrop-filter: blur(10px); + box-shadow: #223223aa 1px 1px 4px; } diff --git a/static/pages/shuffle/index.js b/static/pages/shuffle/index.js index 7bfd33e..6d65788 100644 --- a/static/pages/shuffle/index.js +++ b/static/pages/shuffle/index.js @@ -1,17 +1,113 @@ import * as sfw from 'sfw'; -const { Div } = sfw.element.native; +const { Div, Canvas } = sfw.element.native; + +import { Image } from 'widgets'; const css = await sfw.css(import.meta.url, './index.css'); export default class ShuffleView extends sfw.element.Container { + #front_image + #back_image + #front_image_index + #back_image_index + #images + #date + #drag_start_position + #drag_strength + constructor() { super({ css }); + this.#front_image_index = null; + this.#back_image_index = null; + this.body.append( - Div.new({ - id: 'container', - innerText: 'Pure Emptiness', - }) + this.#date = Div.new({ id: 'date' }), + this.#front_image = Image.new({ + id: 'front', + disabled: true, + ontouchstart: (e) => { + if (e.touches.length != 1) return; + + this.#drag_start_position = [e.touches[0].pageX, e.touches[0].pageY]; + }, + ontouchmove: (e) => { + if (e.touches.length != 1) return; + + const position = [e.touches[0].pageX, e.touches[0].pageY]; + + const delta = [ + position[0] - this.#drag_start_position[0], + position[1] - this.#drag_start_position[1] + ]; + + const strength = Math.min(1, Math.max((delta[0]**2 + delta[1]**2) / (40 ** 2), 0) / 100); + this.#drag_strength = strength; + console.log(this.#drag_strength); + + const angle_x = position[0] - window.screen.width / 2; + const angle_y = position[1] - window.screen.height * 1.2; + let angle = (Math.atan((angle_y) / (angle_x)) * 180 / Math.PI + 90); + + if (angle_x < 0) { + angle -= 180; + } + + this.#front_image.style.transform = ` + translate(-50%, calc(-50% - 50px)) + translate(${delta[0]}px, ${delta[1]}px) + rotate(${angle}deg) + `; + this.#back_image.style.filter = `blur(${(1 - strength) * 50}px)`; + }, + ontouchend: (e) => { + if (this.#drag_strength > 0.3) { + this.next(); + } + + this.#front_image.style.transform = ''; + this.#back_image.style.filter = ''; + }, + }), + this.#back_image = Image.new({ id: 'back', disabled: true }), ) + + this.ondragover = (e) => { + console.log(e) + } + + this.onclick = () => this.next(); + } + + set images(images) { + this.#images = images; + } + + open() { + this.next(); + this.next(); + } + + next() { + this.#front_image_index = this.#back_image_index; + this.#back_image_index = Math.floor(Math.random() * this.#images.length); + + if (this.#front_image_index !== null) { + this.#front_image.metadata = this.#images[this.#front_image_index]; + this.#front_image.load(); + + const date = this.#images[this.#front_image_index].date + + this.#date.innerText = date.toLocaleString('default', { + month: 'long', + year: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric' + }); ; + } + + this.#back_image.metadata = this.#images[this.#back_image_index]; + this.#back_image.load(); } } diff --git a/static/widgets/image/index.css b/static/widgets/image/index.css index 063e5b1..bfca517 100644 --- a/static/widgets/image/index.css +++ b/static/widgets/image/index.css @@ -3,7 +3,7 @@ height: 100%; min-height: 100px; border-radius: var(--border-radius); - overflow: none; + overflow: hidden; box-shadow: #223223aa 1px 1px 4px; background: var(--card-background); } diff --git a/static/widgets/image/index.js b/static/widgets/image/index.js index 76419ea..3e403bd 100644 --- a/static/widgets/image/index.js +++ b/static/widgets/image/index.js @@ -13,10 +13,13 @@ export default class Image extends sfw.element.Container { #month #date #id_element + #disabled constructor() { super ({ css }) + this.#disabled = false; + this.body.append( this.#container = Div.new({ id: 'container', @@ -70,8 +73,10 @@ export default class Image extends sfw.element.Container { }) ], ondblclick: (e) => { - this.#menu.classList.toggle('open'); - e.preventDefault(); + if (!this.#disabled) { + this.#menu.classList.toggle('open'); + e.preventDefault(); + } } }) ); @@ -93,4 +98,9 @@ export default class Image extends sfw.element.Container { get month() { return this.#month; } + + set disabled(value) { + this.#disabled = value; + this.#image.setAttribute('draggable', !value); + } } diff --git a/static/widgets/index.js b/static/widgets/index.js index c468f34..6220318 100644 --- a/static/widgets/index.js +++ b/static/widgets/index.js @@ -1,6 +1,6 @@ -export Editable from './editable/index.js'; -export Image from './image/index.js'; -export MonthSelect from './month-select/index.js'; -export PasswordDialog from './password-dialog/index.js'; -export Search from './search/index.js'; -export UploadBar from './upload-bar/index.js'; +export { default as Editable } from './editable/index.js'; +export { default as Image } from './image/index.js'; +export { default as MonthSelect } from './month-select/index.js'; +export { default as PasswordDialog } from './password-dialog/index.js'; +export { default as Search } from './search/index.js'; +export { default as UploadBar } from './upload-bar/index.js'; |