mirror of
https://github.com/Sped0n/bridget.git
synced 2026-04-17 03:29:31 -07:00
Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1acf24a519 | ||
|
|
e2afe91131 | ||
|
|
3e51b96825 | ||
|
|
c84b4cf234 | ||
|
|
997207fa90 | ||
|
|
f7d2c7962c | ||
|
|
0812a5a6b8 | ||
|
|
7fd971eb13 | ||
|
|
881b0b6490 | ||
|
|
50d7b14133 | ||
|
|
872d23ad13 | ||
|
|
eeca83a74b | ||
|
|
3170f5b65a | ||
|
|
bf1c5e21bc | ||
|
|
d08e2c92b8 | ||
|
|
ba07636f8f | ||
|
|
a98c6a4a60 | ||
|
|
c1414bbfc5 | ||
|
|
22b81a8e1d | ||
|
|
8432540bde | ||
|
|
bb056d9c4f | ||
|
|
6bf10c103f | ||
|
|
1b1aea5047 | ||
|
|
5132e36e87 | ||
|
|
964c1802d3 | ||
|
|
0af4e20720 | ||
|
|
1f65b08b56 | ||
|
|
9bfaac25f5 | ||
|
|
794b5c0ea6 | ||
|
|
2fd34c2f7f | ||
|
|
44d7da48e3 | ||
|
|
49e9f904e2 | ||
|
|
1a3fade5fc | ||
|
|
4e4c32384a | ||
|
|
88b518b773 | ||
|
|
4eece4684a | ||
|
|
4f9b5ff311 | ||
|
|
c123fd45be | ||
|
|
09506d2d4e | ||
|
|
08350cfb9f | ||
|
|
cf2f36d232 | ||
|
|
480df04e55 | ||
|
|
ae96a07793 | ||
|
|
17ef30c18b | ||
|
|
0717ce1051 | ||
|
|
9fa1b718b8 | ||
|
|
8c6b38bb49 | ||
|
|
80c784262b | ||
|
|
ae1a08eb82 | ||
|
|
ff1a76eef8 | ||
|
|
d9452ca8d2 | ||
|
|
3fef127666 | ||
|
|
ed3b08dce2 | ||
|
|
8ba41fc32c | ||
|
|
0f537630e8 | ||
|
|
3f60289381 | ||
|
|
a3c2310375 | ||
|
|
b9de1b9c70 | ||
|
|
ac5fb33f41 | ||
|
|
ba044a2147 | ||
|
|
b34a85fa92 | ||
|
|
7b0cd6aae7 | ||
|
|
bc04ef37bb | ||
|
|
a5807f7625 | ||
|
|
0c16bab065 | ||
|
|
30a01cfd63 | ||
|
|
1e2f1fadde | ||
|
|
4932953871 | ||
|
|
44f3187540 | ||
|
|
80d5a2071b | ||
|
|
c02099aba9 | ||
|
|
50b0e6a5f4 | ||
|
|
6e066497f6 | ||
|
|
af664924e5 | ||
|
|
cdebc028cb | ||
|
|
6210fdda61 | ||
|
|
58f4ebe137 | ||
|
|
1381190a1a | ||
|
|
99220ec318 | ||
|
|
44b883ea96 | ||
|
|
8fd8ed52fe | ||
|
|
6a75a9bc83 | ||
|
|
96b38e9e60 | ||
|
|
267e79a090 | ||
|
|
b3ca475134 | ||
|
|
0126dfdd7e | ||
|
|
2c8db4abe1 | ||
|
|
4e0f083840 | ||
|
|
b3e3c7b0d4 | ||
|
|
784716658d | ||
|
|
a8975c5324 | ||
|
|
11c6fc4c0c | ||
|
|
1da9215a79 | ||
|
|
e0bf170cf2 | ||
|
|
ebdf2405f0 | ||
|
|
83370e6e67 | ||
|
|
c2956b7d4b | ||
|
|
619dc05e97 | ||
|
|
5394ea20eb | ||
|
|
39fdbb2921 | ||
|
|
8ddc7c840e | ||
|
|
9b08d255f1 | ||
|
|
b751623530 | ||
|
|
54e715dfd5 | ||
|
|
9c073e98e1 | ||
|
|
83363387cd | ||
|
|
d608016829 | ||
|
|
cd47ac8538 | ||
|
|
3c9f19a506 | ||
|
|
454862fb5f | ||
|
|
fe7923e558 | ||
|
|
565ce4a241 | ||
|
|
f2b319caa2 |
@@ -22,6 +22,7 @@
|
|||||||
"arrow-body-style": "off",
|
"arrow-body-style": "off",
|
||||||
"prefer-arrow-callback": "off",
|
"prefer-arrow-callback": "off",
|
||||||
"import/no-cycle": "error",
|
"import/no-cycle": "error",
|
||||||
|
"@typescript-eslint/non-nullable-type-assertion-style": "off",
|
||||||
"sort-imports": [
|
"sort-imports": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -8,5 +8,5 @@ updates:
|
|||||||
- package-ecosystem: 'npm' # See documentation for possible values
|
- package-ecosystem: 'npm' # See documentation for possible values
|
||||||
directory: '/' # Location of package manifests
|
directory: '/' # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: 'daily'
|
interval: 'weekly'
|
||||||
open-pull-requests-limit: 1000
|
open-pull-requests-limit: 1000
|
||||||
|
|||||||
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -18,13 +18,13 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
any_changed: ${{ steps.changed-files-specific.outputs.any_changed }}
|
any_changed: ${{ steps.changed-files-specific.outputs.any_changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Get changed files in scope
|
- name: Get changed files in scope
|
||||||
id: changed-files-specific
|
id: changed-files-specific
|
||||||
uses: tj-actions/changed-files@v40
|
uses: tj-actions/changed-files@v41
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
package.json
|
package.json
|
||||||
@@ -39,8 +39,15 @@ jobs:
|
|||||||
github.ref == 'refs/heads/main' &&
|
github.ref == 'refs/heads/main' &&
|
||||||
github.event.repository.fork == false
|
github.event.repository.fork == false
|
||||||
steps:
|
steps:
|
||||||
|
- name: Set current date as env variable
|
||||||
|
run: |
|
||||||
|
echo "builddate=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.PAT }}
|
||||||
|
|
||||||
- name: Setup Hugo
|
- name: Setup Hugo
|
||||||
uses: peaceiris/actions-hugo@v2
|
uses: peaceiris/actions-hugo@v2
|
||||||
@@ -79,7 +86,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Push artifacts
|
- name: Push artifacts
|
||||||
if: ${{ (github.event_name == 'push' || github.event.pull_request.merged == true) && needs.filter.outputs.any_changed == 'true' }}
|
if: ${{ (github.event_name == 'push' || github.event.pull_request.merged == true) && needs.filter.outputs.any_changed == 'true' }}
|
||||||
run: |
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
git config --global user.email "noreply@github.com"
|
with:
|
||||||
git config --global user.name "CI"
|
commit_message: 'ci: update bundled artifacts [skip ci]'
|
||||||
git diff --quiet || (git add -A && git commit -m "build: update bundled artifacts [skip ci]" && git push)
|
|
||||||
|
|||||||
19
.github/workflows/eslint.yml
vendored
19
.github/workflows/eslint.yml
vendored
@@ -17,7 +17,9 @@ jobs:
|
|||||||
name: Lint
|
name: Lint
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.PAT }}
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v2
|
||||||
@@ -38,5 +40,18 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint Check
|
||||||
|
continue-on-error: true
|
||||||
|
id: check
|
||||||
run: pnpm run lint:check
|
run: pnpm run lint:check
|
||||||
|
|
||||||
|
- name: Format manually
|
||||||
|
id: format
|
||||||
|
if: ${{ steps.check.outcome == 'failure' }}
|
||||||
|
run: pnpm run lint
|
||||||
|
|
||||||
|
- name: Commit
|
||||||
|
if: ${{ steps.format.outcome == 'success' }}
|
||||||
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
|
with:
|
||||||
|
commit_message: 'ci: format code'
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -21,4 +21,7 @@ $RECYCLE.BIN/
|
|||||||
|
|
||||||
# Hugo
|
# Hugo
|
||||||
.hugo_build.lock
|
.hugo_build.lock
|
||||||
jsconfig.json
|
jsconfig.json
|
||||||
|
|
||||||
|
# css map
|
||||||
|
*.css.map
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
node_modules
|
node_modules
|
||||||
static
|
static
|
||||||
exmapleSite
|
exmapleSite
|
||||||
*.yaml
|
single.json
|
||||||
*.yml
|
pnpm-lock.yaml
|
||||||
|
|||||||
@@ -17,3 +17,7 @@ a,
|
|||||||
button {
|
button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Geist';
|
font-family: 'Geist';
|
||||||
src: url('/lib/fonts/GeistVF.woff2') format('woff2');
|
src:
|
||||||
font-weight: 90;
|
url('/lib/fonts/GeistVF.woff2') format('woff2 supports variations'),
|
||||||
|
url('/lib/fonts/GeistVF.woff2') format('woff2-variations');
|
||||||
|
font-weight: 400;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@@ -45,3 +47,11 @@
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'FW';
|
||||||
|
src: url('/lib/fonts/fw.woff2') format('woff2');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ body {
|
|||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
font-family: 'Noto Sans', sans-serif;
|
font-family: 'FW';
|
||||||
}
|
}
|
||||||
|
|
||||||
@include min-width('tablet') {
|
@include min-width('tablet') {
|
||||||
@@ -17,15 +17,43 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body:lang(en, de, es, fr, it) {
|
body:lang(en) {
|
||||||
font-family: 'Geist', sans-serif;
|
font-family: 'Geist', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
body:lang(zh-cn, zh-sg) {
|
body:lang(de) {
|
||||||
|
font-family: 'Geist', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:lang(es) {
|
||||||
|
font-family: 'Geist', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:lang(fr) {
|
||||||
|
font-family: 'Geist', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:lang(it) {
|
||||||
|
font-family: 'Geist', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:lang(zh-cn) {
|
||||||
font-family: 'Noto Sans', 'Noto Sans CJK SC', sans-serif;
|
font-family: 'Noto Sans', 'Noto Sans CJK SC', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
body:lang(zh-hk, zh-mo, zh-tw) {
|
body:lang(zh-sg) {
|
||||||
|
font-family: 'Noto Sans', 'Noto Sans CJK SC', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:lang(zh-hk) {
|
||||||
|
font-family: 'Noto Sans', 'Noto Sans CJK TC', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:lang(zh-mo) {
|
||||||
|
font-family: 'Noto Sans', 'Noto Sans CJK TC', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:lang(zh-tw) {
|
||||||
font-family: 'Noto Sans', 'Noto Sans CJK TC', sans-serif;
|
font-family: 'Noto Sans', 'Noto Sans CJK TC', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.info {
|
article {
|
||||||
padding: var(--space-standard);
|
padding: var(--space-standard);
|
||||||
max-width: 25em;
|
max-width: 25em;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $tablet), (hover: none) {
|
@media (max-width: $tablet), (hover: none) {
|
||||||
.info {
|
article {
|
||||||
margin-top: var(--nav-height);
|
margin-top: var(--nav-height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
.container {
|
$tablet: map-get($breakpoints, 'tablet') - 1;
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
z-index: 0;
|
|
||||||
|
|
||||||
width: 100vw;
|
@media (max-width: $tablet), (hover: none) {
|
||||||
height: var(--window-height);
|
.container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
z-index: 0;
|
||||||
|
|
||||||
overflow-y: scroll;
|
width: 100vw;
|
||||||
overflow-x: hidden;
|
height: var(--window-height);
|
||||||
background: white;
|
|
||||||
|
|
||||||
overscroll-behavior: none;
|
overflow-y: scroll;
|
||||||
-webkit-overflow-scrolling: none;
|
overflow-x: hidden;
|
||||||
}
|
background: white;
|
||||||
|
|
||||||
.disableScroll {
|
overscroll-behavior: none;
|
||||||
pointer-events: none;
|
-webkit-overflow-scrolling: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disableScroll {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,30 @@
|
|||||||
.galleryInner {
|
.galleryInner {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.swiper-slide {
|
.swiper-slide {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.loadingText {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate3d(-50%, -50%, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slideContainer {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
|
|||||||
@@ -9,3 +9,4 @@
|
|||||||
|
|
||||||
@import '_partial/nav';
|
@import '_partial/nav';
|
||||||
@import '_partial/article';
|
@import '_partial/article';
|
||||||
|
@import '_partial/container';
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
import { scrollable } from './mobile/scroll'
|
import { Watchable } from './globalUtils'
|
||||||
|
|
||||||
export let container: HTMLDivElement
|
export const scrollable = new Watchable<boolean>(true)
|
||||||
|
|
||||||
|
export let container: Container
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interfaces
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface Container extends HTMLDivElement {
|
||||||
|
dataset: {
|
||||||
|
next: string
|
||||||
|
close: string
|
||||||
|
prev: string
|
||||||
|
loading: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function initContainer(): void {
|
export function initContainer(): void {
|
||||||
container = document.getElementsByClassName('container').item(0) as HTMLDivElement
|
container = document.getElementsByClassName('container').item(0) as Container
|
||||||
scrollable.addWatcher((o) => {
|
scrollable.addWatcher((o) => {
|
||||||
if (o) {
|
if (o) {
|
||||||
container.classList.remove('disableScroll')
|
container.classList.remove('disableScroll')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { container } from '../container'
|
import { container } from '../container'
|
||||||
|
|
||||||
import { active } from './stage'
|
import { active } from './state'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* variables
|
* variables
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import { initCustomCursor } from './customCursor'
|
|||||||
import { initStage } from './stage'
|
import { initStage } from './stage'
|
||||||
import { initStageNav } from './stageNav'
|
import { initStageNav } from './stageNav'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* main functions
|
||||||
|
*/
|
||||||
|
|
||||||
export function initDesktop(ijs: ImageJSON[]): void {
|
export function initDesktop(ijs: ImageJSON[]): void {
|
||||||
initCustomCursor()
|
initCustomCursor()
|
||||||
initStage(ijs)
|
initStage(ijs)
|
||||||
|
|||||||
@@ -1,33 +1,26 @@
|
|||||||
import { type Power3, type gsap } from 'gsap'
|
import { type gsap } from 'gsap'
|
||||||
|
|
||||||
import { container } from '../container'
|
import { container } from '../container'
|
||||||
|
import { incIndex, isAnimating, navigateVector, state } from '../globalState'
|
||||||
|
import { decrement, increment, loadGsap } from '../globalUtils'
|
||||||
import { type ImageJSON } from '../resources'
|
import { type ImageJSON } from '../resources'
|
||||||
import { incIndex, state } from '../state'
|
|
||||||
import { Watchable, decrement, increment, loadGsap } from '../utils'
|
|
||||||
|
|
||||||
/**
|
import { active, cordHist, isLoading, isOpen } from './state'
|
||||||
* types
|
// eslint-disable-next-line sort-imports
|
||||||
*/
|
import { onMutation, type DesktopImage } from './utils'
|
||||||
|
|
||||||
export interface HistoryItem {
|
|
||||||
i: number
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* variables
|
* variables
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let imgs: HTMLImageElement[] = []
|
let imgs: DesktopImage[] = []
|
||||||
let last = { x: 0, y: 0 }
|
let last = { x: 0, y: 0 }
|
||||||
export const cordHist = new Watchable<HistoryItem[]>([])
|
|
||||||
export const isOpen = new Watchable<boolean>(false)
|
|
||||||
export const isAnimating = new Watchable<boolean>(false)
|
|
||||||
export const active = new Watchable<boolean>(false)
|
|
||||||
|
|
||||||
let _gsap: typeof gsap
|
let _gsap: typeof gsap
|
||||||
let _Power3: typeof Power3
|
|
||||||
|
/**
|
||||||
|
* state
|
||||||
|
*/
|
||||||
|
|
||||||
let gsapLoaded = false
|
let gsapLoaded = false
|
||||||
|
|
||||||
@@ -35,41 +28,34 @@ let gsapLoaded = false
|
|||||||
* getter
|
* getter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getElTrail(): HTMLImageElement[] {
|
function getTrailElsIndex(): number[] {
|
||||||
return cordHist.get().map((item) => imgs[item.i])
|
return cordHist.get().map((item) => item.i)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElTrailCurrent(): HTMLImageElement[] {
|
function getTrailCurrentElsIndex(): number[] {
|
||||||
return getElTrail().slice(-state.get().trailLength)
|
return getTrailElsIndex().slice(-state.get().trailLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElTrailInactive(): HTMLImageElement[] {
|
function getTrailInactiveElsIndex(): number[] {
|
||||||
const elTrailCurrent = getElTrailCurrent()
|
const trailCurrentElsIndex = getTrailCurrentElsIndex()
|
||||||
return elTrailCurrent.slice(0, elTrailCurrent.length - 1)
|
return trailCurrentElsIndex.slice(0, trailCurrentElsIndex.length - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElCurrent(): HTMLImageElement {
|
function getCurrentElIndex(): number {
|
||||||
const elTrail = getElTrail()
|
const trailElsIndex = getTrailElsIndex()
|
||||||
return elTrail[elTrail.length - 1]
|
return trailElsIndex[trailElsIndex.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElNextFive(): HTMLImageElement[] {
|
function getPrevElIndex(): number {
|
||||||
|
const c = cordHist.get()
|
||||||
const s = state.get()
|
const s = state.get()
|
||||||
const els = []
|
return decrement(c[c.length - 1].i, s.length)
|
||||||
for (let i = 0; i < 5; i++) {
|
|
||||||
els.push(imgs[increment(s.index + i, s.length)])
|
|
||||||
}
|
|
||||||
return els
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElPrev(): HTMLImageElement {
|
function getNextElIndex(): number {
|
||||||
|
const c = cordHist.get()
|
||||||
const s = state.get()
|
const s = state.get()
|
||||||
return imgs[increment(s.index, s.length)]
|
return increment(c[c.length - 1].i, s.length)
|
||||||
}
|
|
||||||
|
|
||||||
function getElNext(): HTMLImageElement {
|
|
||||||
const s = state.get()
|
|
||||||
return imgs[decrement(s.index, s.length)]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +64,11 @@ function getElNext(): HTMLImageElement {
|
|||||||
|
|
||||||
// on mouse
|
// on mouse
|
||||||
function onMouse(e: MouseEvent): void {
|
function onMouse(e: MouseEvent): void {
|
||||||
if (isOpen.get() || isAnimating.get() || !gsapLoaded) return
|
if (isOpen.get() || isAnimating.get()) return
|
||||||
|
if (!gsapLoaded) {
|
||||||
|
loadLib()
|
||||||
|
return
|
||||||
|
}
|
||||||
const cord = { x: e.clientX, y: e.clientY }
|
const cord = { x: e.clientX, y: e.clientY }
|
||||||
const travelDist = Math.hypot(cord.x - last.x, cord.y - last.y)
|
const travelDist = Math.hypot(cord.x - last.x, cord.y - last.y)
|
||||||
|
|
||||||
@@ -91,63 +81,94 @@ function onMouse(e: MouseEvent): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set image position with gsap
|
// set image position with gsap (in both stage and navigation)
|
||||||
function setPositions(): void {
|
function setPositions(): void {
|
||||||
const elTrail = getElTrail()
|
const trailElsIndex = getTrailElsIndex()
|
||||||
if (elTrail.length === 0 || !gsapLoaded) return
|
if (trailElsIndex.length === 0 || !gsapLoaded) return
|
||||||
|
|
||||||
// preload
|
const elsTrail = getImagesWithIndexArray(trailElsIndex)
|
||||||
lores(getElNextFive())
|
|
||||||
|
|
||||||
_gsap.set(elTrail, {
|
// cached state
|
||||||
x: (i: number) => cordHist.get()[i].x - window.innerWidth / 2,
|
const _isOpen = isOpen.get()
|
||||||
y: (i: number) => cordHist.get()[i].y - window.innerHeight / 2,
|
const _cordHist = cordHist.get()
|
||||||
|
const _state = state.get()
|
||||||
|
|
||||||
|
_gsap.set(elsTrail, {
|
||||||
|
x: (i: number) => _cordHist[i].x - window.innerWidth / 2,
|
||||||
|
y: (i: number) => _cordHist[i].y - window.innerHeight / 2,
|
||||||
opacity: (i: number) =>
|
opacity: (i: number) =>
|
||||||
i + 1 + state.get().trailLength <= cordHist.get().length ? 0 : 1,
|
Math.max(
|
||||||
|
(i + 1 + _state.trailLength <= _cordHist.length ? 0 : 1) - (_isOpen ? 1 : 0),
|
||||||
|
0
|
||||||
|
),
|
||||||
zIndex: (i: number) => i,
|
zIndex: (i: number) => i,
|
||||||
scale: 0.6
|
scale: 0.6
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isOpen.get()) {
|
if (_isOpen) {
|
||||||
lores(getElTrail())
|
const elc = getImagesWithIndexArray([getCurrentElIndex()])[0]
|
||||||
hires([getElCurrent()])
|
const indexArrayToHires: number[] = []
|
||||||
_gsap.set(imgs, { opacity: 0 })
|
const indexArrayToCleanup: number[] = []
|
||||||
_gsap.set(getElCurrent(), { opacity: 1, x: 0, y: 0, scale: 1 })
|
switch (navigateVector.get()) {
|
||||||
|
case 'prev':
|
||||||
|
indexArrayToHires.push(getPrevElIndex())
|
||||||
|
indexArrayToCleanup.push(getNextElIndex())
|
||||||
|
break
|
||||||
|
case 'next':
|
||||||
|
indexArrayToHires.push(getNextElIndex())
|
||||||
|
indexArrayToCleanup.push(getPrevElIndex())
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
hires(getImagesWithIndexArray(indexArrayToHires)) // preload
|
||||||
|
_gsap.set(getImagesWithIndexArray(indexArrayToCleanup), { opacity: 0 })
|
||||||
|
_gsap.set(elc, { x: 0, y: 0, scale: 1 }) // set current to center
|
||||||
|
setLoaderForHiresImage(elc) // set loader, if loaded set current opacity to 1
|
||||||
|
} else {
|
||||||
|
lores(elsTrail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// open image into navigation
|
// open image into navigation
|
||||||
function expandImage(): void {
|
function expandImage(): void {
|
||||||
if (isAnimating.get() || !gsapLoaded) return
|
if (isAnimating.get()) return
|
||||||
|
|
||||||
isOpen.set(true)
|
isOpen.set(true)
|
||||||
isAnimating.set(true)
|
isAnimating.set(true)
|
||||||
|
|
||||||
hires([getElCurrent(), getElPrev(), getElNext()])
|
const elcIndex = getCurrentElIndex()
|
||||||
|
const elc = getImagesWithIndexArray([elcIndex])[0]
|
||||||
|
// don't hide here because we want a better transition
|
||||||
|
// elc.classList.add('hide')
|
||||||
|
|
||||||
|
hires(getImagesWithIndexArray([elcIndex, getPrevElIndex(), getNextElIndex()]))
|
||||||
|
setLoaderForHiresImage(elc)
|
||||||
|
|
||||||
const tl = _gsap.timeline()
|
const tl = _gsap.timeline()
|
||||||
|
const trailInactiveEls = getImagesWithIndexArray(getTrailInactiveElsIndex())
|
||||||
// move down and hide trail inactive
|
// move down and hide trail inactive
|
||||||
tl.to(getElTrailInactive(), {
|
tl.to(trailInactiveEls, {
|
||||||
y: '+=20',
|
y: '+=20',
|
||||||
ease: _Power3.easeIn,
|
ease: 'power3.in',
|
||||||
stagger: 0.075,
|
stagger: 0.075,
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
delay: 0.1,
|
delay: 0.1,
|
||||||
opacity: 0
|
opacity: 0
|
||||||
})
|
})
|
||||||
// current move to center
|
// current move to center
|
||||||
tl.to(getElCurrent(), {
|
tl.to(elc, {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
duration: 0.7,
|
duration: 0.7,
|
||||||
delay: 0.3
|
delay: 0.3
|
||||||
})
|
})
|
||||||
// current expand
|
// current expand
|
||||||
tl.to(getElCurrent(), {
|
tl.to(elc, {
|
||||||
delay: 0.1,
|
delay: 0.1,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
ease: _Power3.easeInOut
|
ease: 'power3.inOut'
|
||||||
})
|
})
|
||||||
// finished
|
// finished
|
||||||
tl.then(() => {
|
tl.then(() => {
|
||||||
@@ -159,33 +180,37 @@ function expandImage(): void {
|
|||||||
|
|
||||||
// close navigation and back to stage
|
// close navigation and back to stage
|
||||||
export function minimizeImage(): void {
|
export function minimizeImage(): void {
|
||||||
if (isAnimating.get() || !gsapLoaded) return
|
if (isAnimating.get()) return
|
||||||
|
|
||||||
isOpen.set(false)
|
isOpen.set(false)
|
||||||
isAnimating.set(true)
|
isAnimating.set(true)
|
||||||
|
navigateVector.set('none') // cleanup
|
||||||
|
|
||||||
lores([getElCurrent()])
|
lores(
|
||||||
lores(getElTrailInactive())
|
getImagesWithIndexArray([...getTrailInactiveElsIndex(), ...[getCurrentElIndex()]])
|
||||||
|
)
|
||||||
|
|
||||||
const tl = _gsap.timeline()
|
const tl = _gsap.timeline()
|
||||||
|
const elc = getImagesWithIndexArray([getCurrentElIndex()])[0]
|
||||||
|
const elsTrailInactive = getImagesWithIndexArray(getTrailInactiveElsIndex())
|
||||||
// shrink current
|
// shrink current
|
||||||
tl.to(getElCurrent(), {
|
tl.to(elc, {
|
||||||
scale: 0.6,
|
scale: 0.6,
|
||||||
duration: 0.6,
|
duration: 0.6,
|
||||||
ease: _Power3.easeInOut
|
ease: 'power3.inOut'
|
||||||
})
|
})
|
||||||
// move current to original position
|
// move current to original position
|
||||||
tl.to(getElCurrent(), {
|
tl.to(elc, {
|
||||||
delay: 0.3,
|
delay: 0.3,
|
||||||
duration: 0.7,
|
duration: 0.7,
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
x: cordHist.get()[cordHist.get().length - 1].x - window.innerWidth / 2,
|
x: cordHist.get()[cordHist.get().length - 1].x - window.innerWidth / 2,
|
||||||
y: cordHist.get()[cordHist.get().length - 1].y - window.innerHeight / 2
|
y: cordHist.get()[cordHist.get().length - 1].y - window.innerHeight / 2
|
||||||
})
|
})
|
||||||
// show trail inactive
|
// show trail inactive
|
||||||
tl.to(getElTrailInactive(), {
|
tl.to(elsTrailInactive, {
|
||||||
y: '-=20',
|
y: '-=20',
|
||||||
ease: _Power3.easeOut,
|
ease: 'power3.out',
|
||||||
stagger: -0.1,
|
stagger: -0.1,
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
opacity: 1
|
opacity: 1
|
||||||
@@ -208,14 +233,44 @@ export function initStage(ijs: ImageJSON[]): void {
|
|||||||
// get stage
|
// get stage
|
||||||
const stage = document.getElementsByClassName('stage').item(0) as HTMLDivElement
|
const stage = document.getElementsByClassName('stage').item(0) as HTMLDivElement
|
||||||
// get image elements
|
// get image elements
|
||||||
imgs = Array.from(stage.getElementsByTagName('img'))
|
imgs = Array.from(stage.getElementsByTagName('img')) as DesktopImage[]
|
||||||
|
imgs.forEach((img, i) => {
|
||||||
|
// preload first 5 images on page load
|
||||||
|
if (i < 5) {
|
||||||
|
img.src = img.dataset.loUrl
|
||||||
|
}
|
||||||
|
// lores preloader for rest of the images
|
||||||
|
onMutation(img, (mutation) => {
|
||||||
|
// if open or animating, hold
|
||||||
|
if (isOpen.get() || isAnimating.get()) return false
|
||||||
|
// if mutation is not about style attribute, hold
|
||||||
|
if (mutation.attributeName !== 'style') return false
|
||||||
|
const opacity = parseFloat(img.style.opacity)
|
||||||
|
// if opacity is not 1, hold
|
||||||
|
if (opacity !== 1) return false
|
||||||
|
// preload the i + 5th image, if it exists
|
||||||
|
if (i + 5 < imgs.length) {
|
||||||
|
imgs[i + 5].src = imgs[i + 5].dataset.loUrl
|
||||||
|
}
|
||||||
|
// triggered
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
})
|
||||||
// event listeners
|
// event listeners
|
||||||
stage.addEventListener('click', () => {
|
stage.addEventListener(
|
||||||
expandImage()
|
'click',
|
||||||
})
|
() => {
|
||||||
stage.addEventListener('keydown', () => {
|
expandImage()
|
||||||
expandImage()
|
},
|
||||||
})
|
{ passive: true }
|
||||||
|
)
|
||||||
|
stage.addEventListener(
|
||||||
|
'keydown',
|
||||||
|
() => {
|
||||||
|
expandImage()
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
window.addEventListener('mousemove', onMouse, { passive: true })
|
window.addEventListener('mousemove', onMouse, { passive: true })
|
||||||
// watchers
|
// watchers
|
||||||
isOpen.addWatcher((o) => {
|
isOpen.addWatcher((o) => {
|
||||||
@@ -227,21 +282,11 @@ export function initStage(ijs: ImageJSON[]): void {
|
|||||||
cordHist.addWatcher((_) => {
|
cordHist.addWatcher((_) => {
|
||||||
setPositions()
|
setPositions()
|
||||||
})
|
})
|
||||||
// preload
|
|
||||||
lores(getElNextFive())
|
|
||||||
// dynamic import
|
// dynamic import
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
'mousemove',
|
'mousemove',
|
||||||
() => {
|
() => {
|
||||||
loadGsap()
|
loadLib()
|
||||||
.then((g) => {
|
|
||||||
_gsap = g[0]
|
|
||||||
_Power3 = g[1]
|
|
||||||
gsapLoaded = true
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.log(e)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
{ once: true, passive: true }
|
{ once: true, passive: true }
|
||||||
)
|
)
|
||||||
@@ -257,7 +302,7 @@ function createStage(ijs: ImageJSON[]): void {
|
|||||||
stage.className = 'stage'
|
stage.className = 'stage'
|
||||||
// append images to container
|
// append images to container
|
||||||
for (const ij of ijs) {
|
for (const ij of ijs) {
|
||||||
const e = document.createElement('img')
|
const e = document.createElement('img') as DesktopImage
|
||||||
e.height = ij.loImgH
|
e.height = ij.loImgH
|
||||||
e.width = ij.loImgW
|
e.width = ij.loImgW
|
||||||
// set data attributes
|
// set data attributes
|
||||||
@@ -268,23 +313,94 @@ function createStage(ijs: ImageJSON[]): void {
|
|||||||
e.dataset.loImgH = ij.loImgH.toString()
|
e.dataset.loImgH = ij.loImgH.toString()
|
||||||
e.dataset.loImgW = ij.loImgW.toString()
|
e.dataset.loImgW = ij.loImgW.toString()
|
||||||
e.alt = ij.alt
|
e.alt = ij.alt
|
||||||
|
// append
|
||||||
stage.append(e)
|
stage.append(e)
|
||||||
}
|
}
|
||||||
container.append(stage)
|
container.append(stage)
|
||||||
}
|
}
|
||||||
|
|
||||||
function hires(imgs: HTMLImageElement[]): void {
|
function getImagesWithIndexArray(indexArray: number[]): DesktopImage[] {
|
||||||
|
return indexArray.map((i) => imgs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
function hires(imgs: DesktopImage[]): void {
|
||||||
imgs.forEach((img) => {
|
imgs.forEach((img) => {
|
||||||
img.src = img.dataset.hiUrl as string
|
if (img.src === img.dataset.hiUrl) return
|
||||||
img.height = parseInt(img.dataset.hiImgH as string)
|
img.src = img.dataset.hiUrl
|
||||||
img.width = parseInt(img.dataset.hiImgW as string)
|
img.height = parseInt(img.dataset.hiImgH)
|
||||||
|
img.width = parseInt(img.dataset.hiImgW)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function lores(imgs: HTMLImageElement[]): void {
|
function lores(imgs: DesktopImage[]): void {
|
||||||
imgs.forEach((img) => {
|
imgs.forEach((img) => {
|
||||||
img.src = img.dataset.loUrl as string
|
if (img.src === img.dataset.loUrl) return
|
||||||
img.height = parseInt(img.dataset.loImgH as string)
|
img.src = img.dataset.loUrl
|
||||||
img.width = parseInt(img.dataset.loImgW as string)
|
img.height = parseInt(img.dataset.loImgH)
|
||||||
|
img.width = parseInt(img.dataset.loImgW)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setLoaderForHiresImage(e: HTMLImageElement): void {
|
||||||
|
if (!e.complete) {
|
||||||
|
isLoading.set(true)
|
||||||
|
// abort controller for cleanup
|
||||||
|
const controller = new AbortController()
|
||||||
|
const abortSignal = controller.signal
|
||||||
|
// event listeners
|
||||||
|
e.addEventListener(
|
||||||
|
'load',
|
||||||
|
() => {
|
||||||
|
_gsap
|
||||||
|
.to(e, { opacity: 1, ease: 'power3.out', duration: 0.5 })
|
||||||
|
.then(() => {
|
||||||
|
isLoading.set(false)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
controller.abort()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ once: true, passive: true, signal: abortSignal }
|
||||||
|
)
|
||||||
|
e.addEventListener(
|
||||||
|
'error',
|
||||||
|
() => {
|
||||||
|
_gsap
|
||||||
|
.set(e, { opacity: 1 })
|
||||||
|
.then(() => {
|
||||||
|
isLoading.set(false)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
controller.abort()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ once: true, passive: true, signal: abortSignal }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_gsap
|
||||||
|
.set(e, { opacity: 1 })
|
||||||
|
.then(() => {
|
||||||
|
isLoading.set(false)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLib(): void {
|
||||||
|
loadGsap()
|
||||||
|
.then((g) => {
|
||||||
|
_gsap = g
|
||||||
|
gsapLoaded = true
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { container } from '../container'
|
import { container } from '../container'
|
||||||
import { decIndex, incIndex, state } from '../state'
|
import { decIndex, incIndex, isAnimating, navigateVector, state } from '../globalState'
|
||||||
import { decrement, increment } from '../utils'
|
import { decrement, increment } from '../globalUtils'
|
||||||
|
|
||||||
import { setCustomCursor } from './customCursor'
|
import { setCustomCursor } from './customCursor'
|
||||||
import { active, cordHist, isAnimating, isOpen, minimizeImage } from './stage'
|
import { minimizeImage } from './stage'
|
||||||
|
import { active, cordHist, isLoading, isOpen } from './state'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* types
|
* types
|
||||||
@@ -15,12 +16,13 @@ type NavItem = (typeof navItems)[number]
|
|||||||
* variables
|
* variables
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const mainDiv = document.getElementById('main') as HTMLDivElement
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
mainDiv.getAttribute('prevText') as string,
|
container.dataset.prev,
|
||||||
mainDiv.getAttribute('closeText') as string,
|
container.dataset.close,
|
||||||
mainDiv.getAttribute('nextText') as string
|
container.dataset.next
|
||||||
] as const
|
] as const
|
||||||
|
const loadingText = container.dataset.loading + '...'
|
||||||
|
let loadedText = ''
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main functions
|
* main functions
|
||||||
@@ -56,39 +58,80 @@ function handleKey(e: KeyboardEvent): void {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function initStageNav(): void {
|
export function initStageNav(): void {
|
||||||
|
// isLoading
|
||||||
|
isLoading.addWatcher((o) => {
|
||||||
|
if (o) setCustomCursor(loadingText)
|
||||||
|
else setCustomCursor(loadedText)
|
||||||
|
})
|
||||||
|
// navOverlay
|
||||||
const navOverlay = document.createElement('div')
|
const navOverlay = document.createElement('div')
|
||||||
navOverlay.className = 'navOverlay'
|
navOverlay.className = 'navOverlay'
|
||||||
for (const navItem of navItems) {
|
for (const [index, navItem] of navItems.entries()) {
|
||||||
const overlay = document.createElement('div')
|
const overlay = document.createElement('div')
|
||||||
overlay.className = 'overlay'
|
overlay.className = 'overlay'
|
||||||
overlay.addEventListener(
|
const isClose = index === 1
|
||||||
'click',
|
// close
|
||||||
() => {
|
if (isClose) {
|
||||||
handleClick(navItem)
|
overlay.addEventListener(
|
||||||
},
|
'click',
|
||||||
{ passive: true }
|
() => {
|
||||||
)
|
handleCloseClick(navItem)
|
||||||
overlay.addEventListener(
|
},
|
||||||
'keydown',
|
{ passive: true }
|
||||||
() => {
|
)
|
||||||
handleClick(navItem)
|
overlay.addEventListener(
|
||||||
},
|
'keydown',
|
||||||
{ passive: true }
|
() => {
|
||||||
)
|
handleCloseClick(navItem)
|
||||||
overlay.addEventListener(
|
},
|
||||||
'mouseover',
|
{ passive: true }
|
||||||
() => {
|
)
|
||||||
setCustomCursor(navItem)
|
overlay.addEventListener(
|
||||||
},
|
'mouseover',
|
||||||
{ passive: true }
|
() => {
|
||||||
)
|
handleCloseHover(navItem)
|
||||||
overlay.addEventListener(
|
},
|
||||||
'focus',
|
{ passive: true }
|
||||||
() => {
|
)
|
||||||
setCustomCursor(navItem)
|
overlay.addEventListener(
|
||||||
},
|
'focus',
|
||||||
{ passive: true }
|
() => {
|
||||||
)
|
handleCloseHover(navItem)
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// prev and next
|
||||||
|
else {
|
||||||
|
overlay.addEventListener(
|
||||||
|
'click',
|
||||||
|
() => {
|
||||||
|
handlePNClick(navItem)
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
|
overlay.addEventListener(
|
||||||
|
'keydown',
|
||||||
|
() => {
|
||||||
|
handlePNClick(navItem)
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
|
overlay.addEventListener(
|
||||||
|
'mouseover',
|
||||||
|
() => {
|
||||||
|
handlePNHover(navItem)
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
|
overlay.addEventListener(
|
||||||
|
'focus',
|
||||||
|
() => {
|
||||||
|
handlePNHover(navItem)
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
navOverlay.append(overlay)
|
navOverlay.append(overlay)
|
||||||
}
|
}
|
||||||
active.addWatcher(() => {
|
active.addWatcher(() => {
|
||||||
@@ -108,6 +151,7 @@ export function initStageNav(): void {
|
|||||||
|
|
||||||
function nextImage(): void {
|
function nextImage(): void {
|
||||||
if (isAnimating.get()) return
|
if (isAnimating.get()) return
|
||||||
|
navigateVector.set('next')
|
||||||
cordHist.set(
|
cordHist.set(
|
||||||
cordHist.get().map((item) => {
|
cordHist.get().map((item) => {
|
||||||
return { ...item, i: increment(item.i, state.get().length) }
|
return { ...item, i: increment(item.i, state.get().length) }
|
||||||
@@ -119,6 +163,7 @@ function nextImage(): void {
|
|||||||
|
|
||||||
function prevImage(): void {
|
function prevImage(): void {
|
||||||
if (isAnimating.get()) return
|
if (isAnimating.get()) return
|
||||||
|
navigateVector.set('prev')
|
||||||
cordHist.set(
|
cordHist.set(
|
||||||
cordHist.get().map((item) => {
|
cordHist.get().map((item) => {
|
||||||
return { ...item, i: decrement(item.i, state.get().length) }
|
return { ...item, i: decrement(item.i, state.get().length) }
|
||||||
@@ -127,3 +172,23 @@ function prevImage(): void {
|
|||||||
|
|
||||||
decIndex()
|
decIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCloseClick(navItem: NavItem): void {
|
||||||
|
handleClick(navItem)
|
||||||
|
isLoading.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCloseHover(navItem: NavItem): void {
|
||||||
|
loadedText = navItem
|
||||||
|
setCustomCursor(navItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePNClick(navItem: NavItem): void {
|
||||||
|
if (!isLoading.get()) handleClick(navItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePNHover(navItem: NavItem): void {
|
||||||
|
loadedText = navItem
|
||||||
|
if (isLoading.get()) setCustomCursor(loadingText)
|
||||||
|
else setCustomCursor(navItem)
|
||||||
|
}
|
||||||
|
|||||||
20
assets/ts/desktop/state.ts
Normal file
20
assets/ts/desktop/state.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Watchable } from '../globalUtils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* types
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface HistoryItem {
|
||||||
|
i: number
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const cordHist = new Watchable<HistoryItem[]>([])
|
||||||
|
export const isOpen = new Watchable<boolean>(false)
|
||||||
|
export const active = new Watchable<boolean>(false)
|
||||||
|
export const isLoading = new Watchable<boolean>(false)
|
||||||
33
assets/ts/desktop/utils.ts
Normal file
33
assets/ts/desktop/utils.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* interfaces
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface DesktopImage extends HTMLImageElement {
|
||||||
|
dataset: {
|
||||||
|
hiUrl: string
|
||||||
|
hiImgH: string
|
||||||
|
hiImgW: string
|
||||||
|
loUrl: string
|
||||||
|
loImgH: string
|
||||||
|
loImgW: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* utils
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function onMutation<T extends HTMLElement>(
|
||||||
|
element: T,
|
||||||
|
trigger: (arg0: MutationRecord) => boolean,
|
||||||
|
observeOptions: MutationObserverInit = { attributes: true }
|
||||||
|
): void {
|
||||||
|
new MutationObserver((mutations, observer) => {
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
if (trigger(mutation)) {
|
||||||
|
observer.disconnect()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).observe(element, observeOptions)
|
||||||
|
}
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
import { Watchable, decrement, increment } from './utils'
|
import {
|
||||||
|
Watchable,
|
||||||
|
decrement,
|
||||||
|
getThresholdSessionIndex,
|
||||||
|
increment
|
||||||
|
} from './globalUtils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* types
|
* types
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type State = typeof defaultState
|
export type State = typeof defaultState
|
||||||
|
export type NavVec = 'next' | 'none' | 'prev'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* variables
|
* variables
|
||||||
@@ -25,7 +31,9 @@ const defaultState = {
|
|||||||
trailLength: thresholds[getThresholdSessionIndex()].trailLength
|
trailLength: thresholds[getThresholdSessionIndex()].trailLength
|
||||||
}
|
}
|
||||||
|
|
||||||
export const state = new Watchable<State>(defaultState)
|
export const state = new Watchable<State>(defaultState, false)
|
||||||
|
export const isAnimating = new Watchable<boolean>(false)
|
||||||
|
export const navigateVector = new Watchable<NavVec>('none')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main functions
|
* main functions
|
||||||
@@ -81,9 +89,3 @@ function updateThreshold(state: State, inc: number): State {
|
|||||||
const newItems = thresholds[i]
|
const newItems = thresholds[i]
|
||||||
return { ...state, ...newItems }
|
return { ...state, ...newItems }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThresholdSessionIndex(): number {
|
|
||||||
const s = sessionStorage.getItem('thresholdsIndex')
|
|
||||||
if (s === null) return 2
|
|
||||||
return parseInt(s)
|
|
||||||
}
|
|
||||||
69
assets/ts/globalUtils.ts
Normal file
69
assets/ts/globalUtils.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { type gsap } from 'gsap'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* utils
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function increment(num: number, length: number): number {
|
||||||
|
return (num + 1) % length
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decrement(num: number, length: number): number {
|
||||||
|
return (num + length - 1) % length
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expand(num: number): string {
|
||||||
|
return ('0000' + num.toString()).slice(-4)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadGsap(): Promise<typeof gsap> {
|
||||||
|
const g = await import('gsap')
|
||||||
|
return g.gsap
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getThresholdSessionIndex(): number {
|
||||||
|
const s = sessionStorage.getItem('thresholdsIndex')
|
||||||
|
if (s === null) return 2
|
||||||
|
return parseInt(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeDuplicates<T>(arr: T[]): T[] {
|
||||||
|
if (arr.length < 2) return arr // optimization
|
||||||
|
return [...new Set(arr)]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDivWithClass(className: string): HTMLDivElement {
|
||||||
|
const div = document.createElement('div')
|
||||||
|
if (className === '') return div // optimization
|
||||||
|
div.classList.add(className)
|
||||||
|
return div
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* custom "reactive" object
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Watchable<T> {
|
||||||
|
constructor(
|
||||||
|
private obj: T,
|
||||||
|
private readonly lazy: boolean = true
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private readonly watchers: Array<(arg0: T) => void> = []
|
||||||
|
|
||||||
|
get(): T {
|
||||||
|
return this.obj
|
||||||
|
}
|
||||||
|
|
||||||
|
set(e: T): void {
|
||||||
|
if (e === this.obj && this.lazy) return
|
||||||
|
this.obj = e
|
||||||
|
this.watchers.forEach((watcher) => {
|
||||||
|
watcher(this.obj)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
addWatcher(watcher: (arg0: T) => void): void {
|
||||||
|
this.watchers.push(watcher)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,33 @@
|
|||||||
import { initContainer } from './container'
|
import { initContainer } from './container'
|
||||||
|
import { initState } from './globalState'
|
||||||
import { initNav } from './nav'
|
import { initNav } from './nav'
|
||||||
import { initResources } from './resources'
|
import { initResources } from './resources'
|
||||||
import { initState } from './state'
|
|
||||||
import { isMobile } from './utils'
|
|
||||||
|
|
||||||
initContainer()
|
// this is the main entry point for the app
|
||||||
const ijs = initResources()
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
initState(ijs.length)
|
main().catch((e) => {
|
||||||
initNav()
|
console.log(e)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// NOTE: it seems firefox and chromnium don't like top layer await
|
/**
|
||||||
// so we are using import then instead
|
* main functions
|
||||||
if (ijs.length > 0) {
|
*/
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
initContainer()
|
||||||
|
const ijs = await initResources()
|
||||||
|
initState(ijs.length)
|
||||||
|
initNav()
|
||||||
|
|
||||||
|
if (ijs.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: it seems firefox and chromnium don't like top layer await
|
||||||
|
// so we are using import then instead
|
||||||
if (!isMobile()) {
|
if (!isMobile()) {
|
||||||
import('./desktop/init')
|
await import('./desktop/init')
|
||||||
.then((d) => {
|
.then((d) => {
|
||||||
d.initDesktop(ijs)
|
d.initDesktop(ijs)
|
||||||
})
|
})
|
||||||
@@ -21,7 +35,7 @@ if (ijs.length > 0) {
|
|||||||
console.log(e)
|
console.log(e)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
import('./mobile/init')
|
await import('./mobile/init')
|
||||||
.then((m) => {
|
.then((m) => {
|
||||||
m.initMobile(ijs)
|
m.initMobile(ijs)
|
||||||
})
|
})
|
||||||
@@ -30,3 +44,11 @@ if (ijs.length > 0) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hepler
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isMobile(): boolean {
|
||||||
|
return window.matchMedia('(hover: none)').matches
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { container } from '../container'
|
import { container } from '../container'
|
||||||
|
import { setIndex } from '../globalState'
|
||||||
import { type ImageJSON } from '../resources'
|
import { type ImageJSON } from '../resources'
|
||||||
import { setIndex } from '../state'
|
|
||||||
import { getRandom, onVisible } from '../utils'
|
|
||||||
|
|
||||||
import { slideUp } from './gallery'
|
import { slideUp } from './gallery'
|
||||||
import { mounted } from './mounted'
|
import { mounted } from './state'
|
||||||
|
// eslint-disable-next-line sort-imports
|
||||||
|
import { getRandom, onIntersection, type MobileImage } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* variables
|
* variables
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export let imgs: HTMLImageElement[] = []
|
export let imgs: MobileImage[] = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main functions
|
* main functions
|
||||||
@@ -40,9 +41,14 @@ export function initCollection(ijs: ImageJSON[]): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
// get image elements
|
// get image elements
|
||||||
imgs = Array.from(collection.getElementsByTagName('img'))
|
imgs = Array.from(collection.getElementsByTagName('img')) as MobileImage[]
|
||||||
// add event listeners
|
// add event listeners
|
||||||
imgs.forEach((img, i) => {
|
imgs.forEach((img, i) => {
|
||||||
|
// preload first 5 images on page load
|
||||||
|
if (i < 5) {
|
||||||
|
img.src = img.dataset.src
|
||||||
|
}
|
||||||
|
// event listeners
|
||||||
img.addEventListener(
|
img.addEventListener(
|
||||||
'click',
|
'click',
|
||||||
() => {
|
() => {
|
||||||
@@ -58,12 +64,15 @@ export function initCollection(ijs: ImageJSON[]): void {
|
|||||||
{ passive: true }
|
{ passive: true }
|
||||||
)
|
)
|
||||||
// preload
|
// preload
|
||||||
onVisible(img, () => {
|
onIntersection(img, (entry) => {
|
||||||
for (let _i = 0; _i < 5; _i++) {
|
// no intersection, hold
|
||||||
const n = i + _i
|
if (entry.intersectionRatio <= 0) return false
|
||||||
if (n < 0 || n > imgs.length - 1) continue
|
// preload the i + 5th image, if it exists
|
||||||
imgs[n].src = imgs[n].dataset.src as string
|
if (i + 5 < imgs.length) {
|
||||||
|
imgs[i + 5].src = imgs[i + 5].dataset.src
|
||||||
}
|
}
|
||||||
|
// triggered
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -82,7 +91,7 @@ function createCollection(ijs: ImageJSON[]): void {
|
|||||||
const x = i !== 0 ? getRandom(-25, 25) : 0
|
const x = i !== 0 ? getRandom(-25, 25) : 0
|
||||||
const y = i !== 0 ? getRandom(-30, 30) : 0
|
const y = i !== 0 ? getRandom(-30, 30) : 0
|
||||||
// element
|
// element
|
||||||
const e = document.createElement('img')
|
const e = document.createElement('img') as MobileImage
|
||||||
e.dataset.src = ij.loUrl
|
e.dataset.src = ij.loUrl
|
||||||
e.height = ij.loImgH
|
e.height = ij.loImgH
|
||||||
e.width = ij.loImgW
|
e.width = ij.loImgW
|
||||||
|
|||||||
@@ -1,38 +1,36 @@
|
|||||||
import { type Power3, type gsap } from 'gsap'
|
import { type gsap } from 'gsap'
|
||||||
import { type Swiper } from 'swiper'
|
import { type Swiper } from 'swiper'
|
||||||
|
|
||||||
import { container } from '../container'
|
import { container, scrollable } from '../container'
|
||||||
|
import { isAnimating, navigateVector, setIndex, state } from '../globalState'
|
||||||
|
import { createDivWithClass, expand, loadGsap, removeDuplicates } from '../globalUtils'
|
||||||
import { type ImageJSON } from '../resources'
|
import { type ImageJSON } from '../resources'
|
||||||
import { setIndex, state } from '../state'
|
|
||||||
import {
|
|
||||||
Watchable,
|
|
||||||
capitalizeFirstLetter,
|
|
||||||
expand,
|
|
||||||
loadGsap,
|
|
||||||
loadSwiper
|
|
||||||
} from '../utils'
|
|
||||||
|
|
||||||
import { mounted } from './mounted'
|
import { mounted } from './state'
|
||||||
import { scrollable } from './scroll'
|
// eslint-disable-next-line sort-imports
|
||||||
|
import { capitalizeFirstLetter, loadSwiper, type MobileImage } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* variables
|
* variables
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let swiperNode: HTMLDivElement
|
let galleryInner: HTMLDivElement
|
||||||
let gallery: HTMLDivElement
|
let gallery: HTMLDivElement
|
||||||
let curtain: HTMLDivElement
|
let curtain: HTMLDivElement
|
||||||
let swiper: Swiper
|
let indexDiv: HTMLDivElement
|
||||||
const isAnimating = new Watchable<boolean>(false)
|
let navDiv: HTMLDivElement
|
||||||
let lastIndex = -1
|
|
||||||
let indexDispNums: HTMLSpanElement[] = []
|
let indexDispNums: HTMLSpanElement[] = []
|
||||||
let galleryImages: HTMLImageElement[] = []
|
let galleryImages: MobileImage[] = []
|
||||||
let collectionImages: HTMLImageElement[] = []
|
let collectionImages: MobileImage[] = []
|
||||||
|
|
||||||
let _Swiper: typeof Swiper
|
|
||||||
let _gsap: typeof gsap
|
let _gsap: typeof gsap
|
||||||
let _Power3: typeof Power3
|
let _swiper: Swiper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* state
|
||||||
|
*/
|
||||||
|
|
||||||
|
let lastIndex = -1
|
||||||
let libLoaded = false
|
let libLoaded = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,9 +41,6 @@ export function slideUp(): void {
|
|||||||
if (isAnimating.get() || !libLoaded) return
|
if (isAnimating.get() || !libLoaded) return
|
||||||
isAnimating.set(true)
|
isAnimating.set(true)
|
||||||
|
|
||||||
// load active image
|
|
||||||
loadImages()
|
|
||||||
|
|
||||||
_gsap.to(curtain, {
|
_gsap.to(curtain, {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
duration: 1
|
duration: 1
|
||||||
@@ -53,24 +48,26 @@ export function slideUp(): void {
|
|||||||
|
|
||||||
_gsap.to(gallery, {
|
_gsap.to(gallery, {
|
||||||
y: 0,
|
y: 0,
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
duration: 1,
|
duration: 1,
|
||||||
delay: 0.4
|
delay: 0.4
|
||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
// cleanup
|
||||||
scrollable.set(false)
|
scrollable.set(false)
|
||||||
isAnimating.set(false)
|
isAnimating.set(false)
|
||||||
}, 1200)
|
}, 1400)
|
||||||
}
|
}
|
||||||
|
|
||||||
function slideDown(): void {
|
function slideDown(): void {
|
||||||
scrollable.set(true)
|
if (isAnimating.get()) return
|
||||||
|
isAnimating.set(true)
|
||||||
scrollToActive()
|
scrollToActive()
|
||||||
|
|
||||||
_gsap.to(gallery, {
|
_gsap.to(gallery, {
|
||||||
y: '100%',
|
y: '100%',
|
||||||
ease: _Power3.easeInOut,
|
ease: 'power3.inOut',
|
||||||
duration: 1
|
duration: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -79,6 +76,13 @@ function slideDown(): void {
|
|||||||
duration: 1.2,
|
duration: 1.2,
|
||||||
delay: 0.4
|
delay: 0.4
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// cleanup
|
||||||
|
scrollable.set(true)
|
||||||
|
isAnimating.set(false)
|
||||||
|
lastIndex = -1
|
||||||
|
}, 1600)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,29 +91,32 @@ function slideDown(): void {
|
|||||||
|
|
||||||
export function initGallery(ijs: ImageJSON[]): void {
|
export function initGallery(ijs: ImageJSON[]): void {
|
||||||
// create gallery
|
// create gallery
|
||||||
createGallery(ijs)
|
constructGallery(ijs)
|
||||||
// get elements
|
// get elements
|
||||||
indexDispNums = Array.from(
|
indexDispNums = Array.from(
|
||||||
document.getElementsByClassName('nav').item(0)?.getElementsByClassName('num') ?? []
|
indexDiv.getElementsByClassName('num') ?? []
|
||||||
) as HTMLSpanElement[]
|
) as HTMLSpanElement[]
|
||||||
swiperNode = document.getElementsByClassName('galleryInner').item(0) as HTMLDivElement
|
galleryImages = Array.from(gallery.getElementsByTagName('img')) as MobileImage[]
|
||||||
gallery = document.getElementsByClassName('gallery').item(0) as HTMLDivElement
|
|
||||||
curtain = document.getElementsByClassName('curtain').item(0) as HTMLDivElement
|
|
||||||
galleryImages = Array.from(gallery.getElementsByTagName('img'))
|
|
||||||
collectionImages = Array.from(
|
collectionImages = Array.from(
|
||||||
document
|
document
|
||||||
.getElementsByClassName('collection')
|
.getElementsByClassName('collection')
|
||||||
.item(0)
|
.item(0)
|
||||||
?.getElementsByTagName('img') ?? []
|
?.getElementsByTagName('img') ?? []
|
||||||
)
|
) as MobileImage[]
|
||||||
// state watcher
|
// state watcher
|
||||||
state.addWatcher(() => {
|
state.addWatcher((o) => {
|
||||||
const s = state.get()
|
if (o.index === lastIndex)
|
||||||
// change slide only when index is changed
|
return // change slide only when index is changed
|
||||||
if (s.index === lastIndex) return
|
else if (lastIndex === -1)
|
||||||
changeSlide(s.index)
|
navigateVector.set('none') // lastIndex before set
|
||||||
updateIndexText()
|
else if (o.index < lastIndex)
|
||||||
lastIndex = s.index
|
navigateVector.set('prev') // set navigate vector for galleryLoadImages
|
||||||
|
else if (o.index > lastIndex)
|
||||||
|
navigateVector.set('next') // set navigate vector for galleryLoadImages
|
||||||
|
else navigateVector.set('none') // default
|
||||||
|
changeSlide(o.index) // change slide to new index
|
||||||
|
updateIndexText() // update index text
|
||||||
|
lastIndex = o.index // update last index
|
||||||
})
|
})
|
||||||
// mounted watcher
|
// mounted watcher
|
||||||
mounted.addWatcher((o) => {
|
mounted.addWatcher((o) => {
|
||||||
@@ -122,17 +129,15 @@ export function initGallery(ijs: ImageJSON[]): void {
|
|||||||
() => {
|
() => {
|
||||||
loadGsap()
|
loadGsap()
|
||||||
.then((g) => {
|
.then((g) => {
|
||||||
_gsap = g[0]
|
_gsap = g
|
||||||
_Power3 = g[1]
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
})
|
})
|
||||||
loadSwiper()
|
loadSwiper()
|
||||||
.then((s) => {
|
.then((S) => {
|
||||||
_Swiper = s
|
_swiper = new S(galleryInner, { spaceBetween: 20 })
|
||||||
swiper = new _Swiper(swiperNode, { spaceBetween: 20 })
|
_swiper.on('slideChange', ({ realIndex }) => {
|
||||||
swiper.on('slideChange', ({ realIndex }) => {
|
|
||||||
setIndex(realIndex)
|
setIndex(realIndex)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -152,8 +157,8 @@ export function initGallery(ijs: ImageJSON[]): void {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function changeSlide(slide: number): void {
|
function changeSlide(slide: number): void {
|
||||||
loadImages()
|
galleryLoadImages()
|
||||||
swiper.slideTo(slide, 0)
|
_swiper.slideTo(slide, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToActive(): void {
|
function scrollToActive(): void {
|
||||||
@@ -175,7 +180,105 @@ function updateIndexText(): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createGallery(ijs: ImageJSON[]): void {
|
function galleryLoadImages(): void {
|
||||||
|
let activeImagesIndex: number[] = []
|
||||||
|
const currentIndex = state.get().index
|
||||||
|
const nextIndex = Math.min(currentIndex + 1, state.get().length - 1)
|
||||||
|
const prevIndex = Math.max(currentIndex - 1, 0)
|
||||||
|
switch (navigateVector.get()) {
|
||||||
|
case 'next':
|
||||||
|
activeImagesIndex = [nextIndex]
|
||||||
|
break
|
||||||
|
case 'prev':
|
||||||
|
activeImagesIndex = [prevIndex]
|
||||||
|
break
|
||||||
|
case 'none':
|
||||||
|
activeImagesIndex = [currentIndex, nextIndex, prevIndex]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
removeDuplicates(activeImagesIndex).forEach((i) => {
|
||||||
|
const e = galleryImages[i]
|
||||||
|
if (e.src === e.dataset.src) return // already loaded
|
||||||
|
e.src = e.dataset.src
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructGalleryNav(): void {
|
||||||
|
// index
|
||||||
|
indexDiv = document.createElement('div')
|
||||||
|
indexDiv.insertAdjacentHTML(
|
||||||
|
'afterbegin',
|
||||||
|
`<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>
|
||||||
|
<span>/</span>
|
||||||
|
<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>`
|
||||||
|
)
|
||||||
|
// close
|
||||||
|
const closeDiv = document.createElement('div')
|
||||||
|
closeDiv.innerText = capitalizeFirstLetter(container.dataset.close)
|
||||||
|
closeDiv.addEventListener(
|
||||||
|
'click',
|
||||||
|
() => {
|
||||||
|
slideDown()
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
|
closeDiv.addEventListener(
|
||||||
|
'keydown',
|
||||||
|
() => {
|
||||||
|
slideDown()
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
|
// nav
|
||||||
|
navDiv = createDivWithClass('nav')
|
||||||
|
navDiv.append(indexDiv, closeDiv)
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructGalleryInner(ijs: ImageJSON[]): void {
|
||||||
|
// swiper wrapper
|
||||||
|
const swiperWrapper = createDivWithClass('swiper-wrapper')
|
||||||
|
// loading text
|
||||||
|
const loadingText = container.dataset.loading + '...'
|
||||||
|
for (const ij of ijs) {
|
||||||
|
// swiper slide
|
||||||
|
const swiperSlide = createDivWithClass('swiper-slide')
|
||||||
|
// loading indicator
|
||||||
|
const l = createDivWithClass('loadingText')
|
||||||
|
l.innerText = loadingText
|
||||||
|
// img
|
||||||
|
const e = document.createElement('img') as MobileImage
|
||||||
|
e.dataset.src = ij.hiUrl
|
||||||
|
e.height = ij.hiImgH
|
||||||
|
e.width = ij.hiImgW
|
||||||
|
e.alt = ij.alt
|
||||||
|
e.style.opacity = '0'
|
||||||
|
// load event
|
||||||
|
e.addEventListener(
|
||||||
|
'load',
|
||||||
|
() => {
|
||||||
|
if (state.get().index !== ij.index) {
|
||||||
|
_gsap.set(e, { opacity: 1 })
|
||||||
|
_gsap.set(l, { opacity: 0 })
|
||||||
|
} else {
|
||||||
|
_gsap.to(e, { opacity: 1, delay: 0.5, duration: 0.5, ease: 'power3.out' })
|
||||||
|
_gsap.to(l, { opacity: 0, duration: 0.5, ease: 'power3.in' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true, passive: true }
|
||||||
|
)
|
||||||
|
// parent container
|
||||||
|
const p = createDivWithClass('slideContainer')
|
||||||
|
// append
|
||||||
|
p.append(e, l)
|
||||||
|
swiperSlide.append(p)
|
||||||
|
swiperWrapper.append(swiperSlide)
|
||||||
|
}
|
||||||
|
// swiper node
|
||||||
|
galleryInner = createDivWithClass('galleryInner')
|
||||||
|
galleryInner.append(swiperWrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructGallery(ijs: ImageJSON[]): void {
|
||||||
/**
|
/**
|
||||||
* gallery
|
* gallery
|
||||||
* |- galleryInner
|
* |- galleryInner
|
||||||
@@ -189,88 +292,21 @@ function createGallery(ijs: ImageJSON[]): void {
|
|||||||
* |- index
|
* |- index
|
||||||
* |- close
|
* |- close
|
||||||
*/
|
*/
|
||||||
// swiper wrapper
|
|
||||||
const _swiperWrapper = document.createElement('div')
|
|
||||||
_swiperWrapper.className = 'swiper-wrapper'
|
|
||||||
// swiper slide
|
|
||||||
for (const ij of ijs) {
|
|
||||||
const _swiperSlide = document.createElement('div')
|
|
||||||
_swiperSlide.className = 'swiper-slide'
|
|
||||||
// img
|
|
||||||
const e = document.createElement('img')
|
|
||||||
e.dataset.src = ij.hiUrl
|
|
||||||
e.height = ij.hiImgH
|
|
||||||
e.width = ij.hiImgW
|
|
||||||
e.alt = ij.alt
|
|
||||||
// append
|
|
||||||
_swiperSlide.append(e)
|
|
||||||
_swiperWrapper.append(_swiperSlide)
|
|
||||||
}
|
|
||||||
// swiper node
|
|
||||||
const _swiperNode = document.createElement('div')
|
|
||||||
_swiperNode.className = 'galleryInner'
|
|
||||||
_swiperNode.append(_swiperWrapper)
|
|
||||||
// index
|
|
||||||
const _index = document.createElement('div')
|
|
||||||
_index.insertAdjacentHTML(
|
|
||||||
'afterbegin',
|
|
||||||
`<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>
|
|
||||||
<span>/</span>
|
|
||||||
<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>`
|
|
||||||
)
|
|
||||||
// close
|
|
||||||
const _close = document.createElement('div')
|
|
||||||
const str: string = document
|
|
||||||
.getElementById('main')
|
|
||||||
?.getAttribute('closeText') as string
|
|
||||||
_close.innerText = capitalizeFirstLetter(str)
|
|
||||||
_close.addEventListener(
|
|
||||||
'click',
|
|
||||||
() => {
|
|
||||||
slideDown()
|
|
||||||
},
|
|
||||||
{ passive: true }
|
|
||||||
)
|
|
||||||
_close.addEventListener(
|
|
||||||
'keydown',
|
|
||||||
() => {
|
|
||||||
slideDown()
|
|
||||||
},
|
|
||||||
{ passive: true }
|
|
||||||
)
|
|
||||||
// nav
|
|
||||||
const _navDiv = document.createElement('div')
|
|
||||||
_navDiv.className = 'nav'
|
|
||||||
_navDiv.append(_index, _close)
|
|
||||||
// gallery
|
// gallery
|
||||||
const _gallery = document.createElement('div')
|
gallery = createDivWithClass('gallery')
|
||||||
_gallery.className = 'gallery'
|
constructGalleryInner(ijs)
|
||||||
_gallery.append(_swiperNode)
|
constructGalleryNav()
|
||||||
_gallery.append(_navDiv)
|
gallery.append(galleryInner, navDiv)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* curtain
|
* curtain
|
||||||
*/
|
*/
|
||||||
const _curtain = document.createElement('div')
|
curtain = createDivWithClass('curtain')
|
||||||
_curtain.className = 'curtain'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* container
|
* container
|
||||||
* |- gallery
|
* |- gallery
|
||||||
* |- curtain
|
* |- curtain
|
||||||
*/
|
*/
|
||||||
container.append(_gallery, _curtain)
|
container.append(gallery, curtain)
|
||||||
}
|
|
||||||
|
|
||||||
function loadImages(): void {
|
|
||||||
const activeImages: HTMLImageElement[] = []
|
|
||||||
// load current, next, prev image
|
|
||||||
activeImages.push(galleryImages[swiper.activeIndex])
|
|
||||||
activeImages.push(
|
|
||||||
galleryImages[Math.min(swiper.activeIndex + 1, swiper.slides.length)]
|
|
||||||
)
|
|
||||||
activeImages.push(galleryImages[Math.max(swiper.activeIndex - 1, 0)])
|
|
||||||
for (const e of activeImages) {
|
|
||||||
e.src = e.dataset.src as string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { Watchable } from '../utils'
|
|
||||||
|
|
||||||
export const scrollable = new Watchable<boolean>(true)
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
import { Watchable } from '../utils'
|
import { Watchable } from '../globalUtils'
|
||||||
|
|
||||||
export const mounted = new Watchable<boolean>(false)
|
export const mounted = new Watchable<boolean>(false)
|
||||||
42
assets/ts/mobile/utils.ts
Normal file
42
assets/ts/mobile/utils.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { type Swiper } from 'swiper'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interfaces
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface MobileImage extends HTMLImageElement {
|
||||||
|
dataset: {
|
||||||
|
src: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* utils
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function getRandom(min: number, max: number): number {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onIntersection<T extends HTMLElement>(
|
||||||
|
element: T,
|
||||||
|
trigger: (arg0: IntersectionObserverEntry) => boolean
|
||||||
|
): void {
|
||||||
|
new IntersectionObserver((entries, observer) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (trigger(entry)) {
|
||||||
|
observer.disconnect()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).observe(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function capitalizeFirstLetter(str: string): string {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadSwiper(): Promise<typeof Swiper> {
|
||||||
|
const s = await import('swiper')
|
||||||
|
return s.Swiper
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { decThreshold, incThreshold, state } from './state'
|
import { decThreshold, incThreshold, state } from './globalState'
|
||||||
import { expand } from './utils'
|
import { expand } from './globalUtils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* variables
|
* variables
|
||||||
@@ -31,37 +31,11 @@ const indexDispNums = Array.from(
|
|||||||
indexDiv.getElementsByClassName('num')
|
indexDiv.getElementsByClassName('num')
|
||||||
) as HTMLSpanElement[]
|
) as HTMLSpanElement[]
|
||||||
|
|
||||||
// links div
|
|
||||||
const linksDiv = document.getElementsByClassName('links').item(0) as HTMLDivElement
|
|
||||||
|
|
||||||
// links
|
|
||||||
const links = Array.from(linksDiv.getElementsByClassName('link')) as HTMLAnchorElement[]
|
|
||||||
|
|
||||||
// current link index
|
|
||||||
const currentLinkIndex = document
|
|
||||||
.getElementById('main')
|
|
||||||
?.getAttribute('currentMenuItemIndex') as string
|
|
||||||
|
|
||||||
// set current link
|
|
||||||
for (const [index, link] of links.entries()) {
|
|
||||||
if (index === parseInt(currentLinkIndex)) {
|
|
||||||
// set current link style
|
|
||||||
link.classList.add('current')
|
|
||||||
// set current link title (only if not home)
|
|
||||||
if (index !== 0) document.title = link.innerText + ' | ' + document.title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init
|
* init
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function initNav(): void {
|
export function initNav(): void {
|
||||||
const s = state.get()
|
|
||||||
// init threshold text
|
|
||||||
updateThresholdText(expand(s.threshold))
|
|
||||||
// init index text
|
|
||||||
updateIndexText(expand(s.index + 1), expand(s.length))
|
|
||||||
// add watcher for updating nav text
|
// add watcher for updating nav text
|
||||||
state.addWatcher((o) => {
|
state.addWatcher((o) => {
|
||||||
updateIndexText(expand(o.index + 1), expand(o.length))
|
updateIndexText(expand(o.index + 1), expand(o.length))
|
||||||
|
|||||||
@@ -10,17 +10,24 @@ export interface ImageJSON {
|
|||||||
hiImgW: number
|
hiImgW: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initResources(): ImageJSON[] {
|
export async function initResources(): Promise<ImageJSON[]> {
|
||||||
const imagesJson = document.getElementById('imagesSource')
|
if (document.title.split(' | ')[0] === '404') {
|
||||||
if (imagesJson === null) {
|
return [] // no images on 404 page
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
return JSON.parse(imagesJson.textContent as string).sort(
|
try {
|
||||||
(a: ImageJSON, b: ImageJSON) => {
|
const response = await fetch(`${window.location.href}index.json`, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const data: ImageJSON[] = await response.json()
|
||||||
|
return data.sort((a: ImageJSON, b: ImageJSON) => {
|
||||||
if (a.index < b.index) {
|
if (a.index < b.index) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
})
|
||||||
)
|
} catch (_) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
import { type Power3, type gsap } from 'gsap'
|
|
||||||
import { type Swiper } from 'swiper'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* custom helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function increment(num: number, length: number): number {
|
|
||||||
return (num + 1) % length
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decrement(num: number, length: number): number {
|
|
||||||
return (num + length - 1) % length
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expand(num: number): string {
|
|
||||||
return ('0000' + num.toString()).slice(-4)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isMobile(): boolean {
|
|
||||||
return window.matchMedia('(hover: none)').matches
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRandom(min: number, max: number): number {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
||||||
}
|
|
||||||
|
|
||||||
export function onVisible<T extends Element>(
|
|
||||||
element: T,
|
|
||||||
callback: (arg0: T) => void
|
|
||||||
): void {
|
|
||||||
new IntersectionObserver((entries, observer) => {
|
|
||||||
entries.forEach((entry) => {
|
|
||||||
if (entry.intersectionRatio > 0) {
|
|
||||||
callback(element)
|
|
||||||
observer.disconnect()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).observe(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function capitalizeFirstLetter(str: string): string {
|
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadGsap(): Promise<[typeof gsap, typeof Power3]> {
|
|
||||||
const g = await import('gsap')
|
|
||||||
return [g.gsap, g.Power3]
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadSwiper(): Promise<typeof Swiper> {
|
|
||||||
const s = await import('swiper')
|
|
||||||
return s.Swiper
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* custom types
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class Watchable<T> {
|
|
||||||
constructor(private obj: T) {}
|
|
||||||
private readonly watchers: Array<(arg0: T) => void> = []
|
|
||||||
|
|
||||||
get(): T {
|
|
||||||
return this.obj
|
|
||||||
}
|
|
||||||
|
|
||||||
set(e: T): void {
|
|
||||||
this.obj = e
|
|
||||||
this.watchers.forEach((watcher) => {
|
|
||||||
watcher(this.obj)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
addWatcher(watcher: (arg0: T) => void): void {
|
|
||||||
this.watchers.push(watcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@ The minimum required Hugo version can be seen in the [`theme.toml`](https://gith
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Git
|
### Git (for adavanced user)
|
||||||
|
|
||||||
On the main branch, you can find the theme's latest source code. To use the latest version, you can clone the repository to `themes/bridget` by running the following command in the root directory of your Hugo site:
|
On the main branch, you can find the theme's latest source code. To use the latest version, you can clone the repository to `themes/bridget` by running the following command in the root directory of your Hugo site:
|
||||||
|
|
||||||
@@ -36,9 +36,13 @@ If you are already using Git for your site, you can add the theme as a submodule
|
|||||||
git submodule add https://github.com/Sped0n/bridget themes/bridget
|
git submodule add https://github.com/Sped0n/bridget themes/bridget
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️⚠️⚠️
|
||||||
|
>
|
||||||
|
> Please refer to the config section for the following content.
|
||||||
|
|
||||||
### Module (recommended)
|
### Module (recommended)
|
||||||
|
|
||||||
> If you want to modify the theme, use Git installation instead.
|
> If you want to have some customizations, use Git installation instead.
|
||||||
|
|
||||||
This theme is also available as a [Hugo module](https://gohugo.io/hugo-modules/). Run the following command in the root directory of your Hugo site:
|
This theme is also available as a [Hugo module](https://gohugo.io/hugo-modules/). Run the following command in the root directory of your Hugo site:
|
||||||
|
|
||||||
@@ -46,7 +50,7 @@ First turn your site into a Hugo module (in case you haven't done it yet):
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
hugo mod init github.com/me/my-new-site
|
hugo mod init github.com/me/my-new-site
|
||||||
# or whatever you like, it doesn’t necessarily have to be a GitHub link.
|
# or whatever you like, it doesn’t necessarily have to be a GitHub repo link.
|
||||||
hugo mod init blablabla
|
hugo mod init blablabla
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,6 +69,10 @@ If you want to upgrade the theme, just run:
|
|||||||
hugo mod get -u
|
hugo mod get -u
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️⚠️⚠️
|
||||||
|
>
|
||||||
|
> Please refer to the config section for the following content.
|
||||||
|
|
||||||
## Content Management
|
## Content Management
|
||||||
|
|
||||||
The content is where the pictures/text is stored, while the static refers to the website icons.
|
The content is where the pictures/text is stored, while the static refers to the website icons.
|
||||||
@@ -104,6 +112,8 @@ menu:
|
|||||||
identifier: Erwitt
|
identifier: Erwitt
|
||||||
title: Erwitt
|
title: Erwitt
|
||||||
unifiedAlt: '© Elliott Erwitt'
|
unifiedAlt: '© Elliott Erwitt'
|
||||||
|
_build:
|
||||||
|
publishResources: false
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -121,6 +131,8 @@ unifiedAlt: '© Elliott Erwitt'
|
|||||||
|
|
||||||
- `unifiedAlt` is **optional**, If you left it empty, the alt attribute of the image will default to its file name; if it is set, the alt attributes of all images will be unified to the value you have set;
|
- `unifiedAlt` is **optional**, If you left it empty, the alt attribute of the image will default to its file name; if it is set, the alt attributes of all images will be unified to the value you have set;
|
||||||
|
|
||||||
|
- `publishResources` is **optional but recommended**, setting it to false will hide unprocessed images in the `public` directory. By default, Hugo’s value for this option is true, which can potentially result in source image leakage.
|
||||||
|
|
||||||
- If this is a **showcase** page, simply place the images in the same directory as index.md.
|
- If this is a **showcase** page, simply place the images in the same directory as index.md.
|
||||||
|
|
||||||
- If this is an **information** page, you can continue writing the information you want to display in index.md.
|
- If this is an **information** page, you can continue writing the information you want to display in index.md.
|
||||||
@@ -155,8 +167,12 @@ replacements = "github.com/Sped0n/bridget -> ../.."
|
|||||||
path = "github.com/Sped0n/bridget"
|
path = "github.com/Sped0n/bridget"
|
||||||
```
|
```
|
||||||
|
|
||||||
- If you want to <u>modify the theme</u> or you have <u>installation with Git</u>, please keep the `replacements` configuration and change the path after the arrow to the location of your local theme file.
|
- If you have _installation with Git_
|
||||||
- If you have <u>installation with Module</u>, remove the `replacements` configuration.
|
|
||||||
|
- `replacement`: replace the _path after the arrow_(`../..`) with the location of your local theme file (⚠️⚠️⚠️**relative path only**, example: `themes/bridget`)
|
||||||
|
- `path`: no change
|
||||||
|
|
||||||
|
- If you have _installation with Module_, **remove the `replacements` configuration**.
|
||||||
|
|
||||||
### `markup.toml`
|
### `markup.toml`
|
||||||
|
|
||||||
@@ -164,8 +180,40 @@ path = "github.com/Sped0n/bridget"
|
|||||||
|
|
||||||
### `params.toml`
|
### `params.toml`
|
||||||
|
|
||||||
There is a detailed description in the comments.
|
Detailed description in the comments.
|
||||||
|
|
||||||
|
> ⚠️⚠️⚠️
|
||||||
|
>
|
||||||
|
> Only thing that you need to pay **extra attention** is the [`bundled`](https://github.com/Sped0n/bridget/blob/1e2f1fadde9c16989eef1ab771f2ac8463dec5a4/exampleSite/config/_default/params.toml#L6) option, please read the corresponding doc and set it as your need.
|
||||||
|
>
|
||||||
|
> For users who have installation with module, please always set this option to `true`, unless you know what you are doing.
|
||||||
|
>
|
||||||
|
> Or you might get the error related to `node_modules/swiper/swiper.scss`.
|
||||||
|
|
||||||
### `sitemap.toml`
|
### `sitemap.toml`
|
||||||
|
|
||||||
https://gohugo.io/templates/sitemap-template/#configuration
|
https://gohugo.io/templates/sitemap-template/#configuration
|
||||||
|
|
||||||
|
## Customization (AKA for developer)
|
||||||
|
|
||||||
|
> Before heading to this section, please make sure you have **installation with Git**.
|
||||||
|
|
||||||
|
### Option 1: _it just works_ way
|
||||||
|
|
||||||
|
> If you want to modify js/ts file, please use option 2.
|
||||||
|
|
||||||
|
1. Use hugo create a site and move the bridget theme into the theme directory.
|
||||||
|
2. Run `npm install` in the _bridget theme root dir_, not _your hugo site root dir_.
|
||||||
|
3. After the command is done, copy the `node_modules` dir from _bridget theme root dir_ to _your hugo site root dir_.
|
||||||
|
4. In _your hugo site root dir_, write/modify configuration files according to your needs, remember to set `bundled` option to `false`, so hugo will not use prebuilt css file.
|
||||||
|
5. Run `hugo server` in _your hugo site root dir_, and you are good to go.
|
||||||
|
|
||||||
|
### Option 2: recommended way
|
||||||
|
|
||||||
|
1. Use hugo create a site and move the bridget theme into the theme directory.
|
||||||
|
2. Run `npm install` in the _bridget theme root dir_, not _your hugo site root dir_.
|
||||||
|
3. Run `npm run dev` in the _bridget theme root dir_, we will use content in exampleSite to debug.
|
||||||
|
4. Make your customization.
|
||||||
|
5. After modification, run `npm run build` in the _bridget theme root dir_ to build artifacts.
|
||||||
|
6. In _your hugo site root dir_, write/modify configuration files according to your needs, remember to set `bundled` option to `true`, so hugo will use the artifacts you built in step 5.
|
||||||
|
7. Run `hugo server` in _your hugo site root dir_, and you are good to go.
|
||||||
|
|||||||
1
exampleSite/config/_default/outputs.toml
Normal file
1
exampleSite/config/_default/outputs.toml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
page = ["HTML", "JSON"]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# description of the site (will be placed in meta)
|
# description of the site (will be placed in meta)
|
||||||
description = "Bridget is a minimal Hugo theme designed for photographers / visual artists."
|
description = "Bridget is a minimal Hugo theme designed for photographers/visual artists."
|
||||||
# use bundled js and css
|
# use bundled js and css
|
||||||
# * if you want to build the js and css from scratch, set this to false and run `npm install` and `npm run build`
|
# * if you want to build the js and css from scratch, set this to false and run `npm install` and `npm run build`
|
||||||
# * tldr: set this to false if you want to develop and edit the js and css
|
# * tldr: set this to false if you want to develop and edit the js and css
|
||||||
@@ -13,6 +13,10 @@ svgFavicon = "/dot.svg"
|
|||||||
# fallback png favicon for unsupported browsers
|
# fallback png favicon for unsupported browsers
|
||||||
svgFaviconFallback = "/dot.png"
|
svgFaviconFallback = "/dot.png"
|
||||||
|
|
||||||
|
# resize options for dynamic resolution, please refer to https://gohugo.io/content-management/image-processing/#image-processing-options
|
||||||
|
loResOpt = "800x webp Lanczos q60"
|
||||||
|
hiResOpt = "2500x webp Lanczos q75"
|
||||||
|
|
||||||
# page config
|
# page config
|
||||||
[page]
|
[page]
|
||||||
# unified alt text for all images in the page
|
# unified alt text for all images in the page
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ menu:
|
|||||||
identifier: Erwitt
|
identifier: Erwitt
|
||||||
title: Erwitt
|
title: Erwitt
|
||||||
unifiedAlt: '© Elliott Erwitt'
|
unifiedAlt: '© Elliott Erwitt'
|
||||||
|
_build:
|
||||||
|
publishResources: false
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ menu:
|
|||||||
identifier: Gruyaert
|
identifier: Gruyaert
|
||||||
title: Gruyaert
|
title: Gruyaert
|
||||||
unifiedAlt: '© Harry Gruyaert'
|
unifiedAlt: '© Harry Gruyaert'
|
||||||
|
_build:
|
||||||
|
publishResources: false
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,18 +8,18 @@ menu:
|
|||||||
identifier: Info
|
identifier: Info
|
||||||
title: Info
|
title: Info
|
||||||
unifiedAlt: ''
|
unifiedAlt: ''
|
||||||
|
_build:
|
||||||
|
publishResources: false
|
||||||
---
|
---
|
||||||
|
|
||||||
Bridget is a _minimal_ Hugo theme designed for photographers / visual artists.
|
Bridget is a _minimal_ Hugo theme designed for photographers/visual artists.
|
||||||
|
|
||||||
The inspiration for this theme came from a video by <u>[Hyperlexed](https://www.youtube.com/@Hyperplexed)</u>, which can be found <u>[here](https://www.youtube.com/watch?v=Jt3A2lNN2aE)</u>. Initially, it was designed using pure TypeScript and CSS. However, after website designer <u>[Tyler McRobert](https://tylermcrobert.com)</u> made the source code publicly available, the whole project was modified to mimic the original design. The animations in Bridget are achieved using gsap and swiper. In essence, it is more like a porting of the original design into Hugo.
|
The inspiration for this theme came from a video by <u>[Hyperlexed](https://www.youtube.com/@Hyperplexed)</u>, which can be found <u>[here](https://www.youtube.com/watch?v=Jt3A2lNN2aE)</u>. Initially, it was developed using no third-party dependencies. However, after website designer <u>[Tyler McRobert](https://tylermcrobert.com)</u> made the source code publicly available, I realized that I have invented many unnecessary wheels, and this project was modified to porting the original design to hugo while focusing on _performance_.
|
||||||
|
|
||||||
Once again, great shout out to <u>[Tyler McRobert](https://tylermcrobert.com)</u> for his inspiration to this project.
|
Once again, great shout out to <u>[Tyler McRobert](https://tylermcrobert.com)</u> for his inspiration to this project.
|
||||||
|
|
||||||
[Github ↗](https://instagram.com/pictures.bridget)
|
[GitHub Repo ↗](https://github.com/Sped0n/bridget)
|
||||||
|
|
||||||
[Instagram ↗](https://www.instagram.com/sped0n/)
|
|
||||||
|
|
||||||
Original site design by <u>[Tyler McRobert](https://tylermcrobert.com)</u>.
|
Original site design by <u>[Tyler McRobert](https://tylermcrobert.com)</u>.
|
||||||
|
|
||||||
© {{< year >}} Spedon | Powered by [Hugo](https://gohugo.io)
|
© {{< year >}} <u>[Spedon](https://github.com/Sped0n)</u> | Powered by [Hugo](https://gohugo.io)
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ menu:
|
|||||||
identifier: Webb
|
identifier: Webb
|
||||||
title: Webb
|
title: Webb
|
||||||
unifiedAlt: '© Alex Webb'
|
unifiedAlt: '© Alex Webb'
|
||||||
|
_build:
|
||||||
|
publishResources: false
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "schließen"
|
|||||||
other = "schwelle"
|
other = "schwelle"
|
||||||
[404]
|
[404]
|
||||||
other = "seite nicht gefunden"
|
other = "seite nicht gefunden"
|
||||||
|
[loading]
|
||||||
|
other = "lade"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "close"
|
|||||||
other = "threshold"
|
other = "threshold"
|
||||||
[404]
|
[404]
|
||||||
other = "page not found"
|
other = "page not found"
|
||||||
|
[loading]
|
||||||
|
other = "loading"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "cerrar"
|
|||||||
other = "umbral"
|
other = "umbral"
|
||||||
[404]
|
[404]
|
||||||
other = "página no encontrada"
|
other = "página no encontrada"
|
||||||
|
[loading]
|
||||||
|
other = "cargando"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "fermer"
|
|||||||
other = "seuil"
|
other = "seuil"
|
||||||
[404]
|
[404]
|
||||||
other = "page non trouvée"
|
other = "page non trouvée"
|
||||||
|
[loading]
|
||||||
|
other = "chargement"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "chiudi"
|
|||||||
other = "soglia"
|
other = "soglia"
|
||||||
[404]
|
[404]
|
||||||
other = "pagina non trovata"
|
other = "pagina non trovata"
|
||||||
|
[loading]
|
||||||
|
other = "caricamento"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "閉じる"
|
|||||||
other = "しきい値"
|
other = "しきい値"
|
||||||
[404]
|
[404]
|
||||||
other = "ページが見つかりません"
|
other = "ページが見つかりません"
|
||||||
|
[loading]
|
||||||
|
other = "読み込み中"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "닫기"
|
|||||||
other = "임계값"
|
other = "임계값"
|
||||||
[404]
|
[404]
|
||||||
other = "페이지를 찾을 수 없습니다"
|
other = "페이지를 찾을 수 없습니다"
|
||||||
|
[loading]
|
||||||
|
other = "로딩중"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "关闭"
|
|||||||
other = "阈值"
|
other = "阈值"
|
||||||
[404]
|
[404]
|
||||||
other = "页面不存在"
|
other = "页面不存在"
|
||||||
|
[loading]
|
||||||
|
other = "加载中"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "關閉"
|
|||||||
other = "閾值"
|
other = "閾值"
|
||||||
[404]
|
[404]
|
||||||
other = "找不到頁面"
|
other = "找不到頁面"
|
||||||
|
[loading]
|
||||||
|
other = "載入中"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "關閉"
|
|||||||
other = "閾值"
|
other = "閾值"
|
||||||
[404]
|
[404]
|
||||||
other = "找不到頁面"
|
other = "找不到頁面"
|
||||||
|
[loading]
|
||||||
|
other = "載入中"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "关闭"
|
|||||||
other = "阈值"
|
other = "阈值"
|
||||||
[404]
|
[404]
|
||||||
other = "页面不存在"
|
other = "页面不存在"
|
||||||
|
[loading]
|
||||||
|
other = "加载中"
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ other = "關閉"
|
|||||||
other = "閾值"
|
other = "閾值"
|
||||||
[404]
|
[404]
|
||||||
other = "找不到頁面"
|
other = "找不到頁面"
|
||||||
|
[loading]
|
||||||
|
other = "載入中"
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
{{- define "main" -}}
|
{{- define "main" -}}
|
||||||
{{- $params := .Scratch.Get "params" -}}
|
|
||||||
{{- $currentPage := . -}}
|
|
||||||
{{- with partial "function/currentMenuItem.html" . -}}
|
|
||||||
{{- partial "resources/imageJSON.html" (dict "Path" .DirName "Page" $currentPage) -}}
|
|
||||||
<script>document.getElementById("main").setAttribute("currentMenuItemIndex", "{{- (sub .ID 1) -}}")</script>
|
|
||||||
{{- end -}}
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{- partial "nav.html" . -}}
|
{{- partial "nav.html" . -}}
|
||||||
|
<article>
|
||||||
|
<p class="error">⛝ <u>404</u> {{- i18n 404 -}} ⛝</p>
|
||||||
|
<p class="error">⛝ <u>404</u> {{- i18n 404 -}} ⛝</p>
|
||||||
|
<p class="error">⛝ <u>404</u> {{- i18n 404 -}} ⛝</p>
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<article class="info">
|
|
||||||
<p class="error">⛝ <u>404</u> {{- i18n 404 -}} ⛝</p>
|
|
||||||
<p class="error">⛝ <u>404</u> {{- i18n 404 -}} ⛝</p>
|
|
||||||
<p class="error">⛝ <u>404</u> {{- i18n 404 -}} ⛝</p>
|
|
||||||
</article>
|
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>{{ site.Title }}</title>
|
{{- partial "head/link.html" . -}}
|
||||||
{{- partial "head/link.html" -}}
|
{{- partial "head/meta.html" . -}}
|
||||||
{{- partial "head/meta.html" -}}
|
{{- partial "head/seo.html" . -}}
|
||||||
{{- partial "head/seo.html" -}}
|
{{- partial "head/favicon.html" . -}}
|
||||||
{{- partial "head/favicon.html" -}}
|
|
||||||
</head>
|
</head>
|
||||||
<body lang="{{- site.LanguageCode -}}">
|
<body lang="{{- site.LanguageCode -}}">
|
||||||
<div id="main">
|
<div id="main">
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
{{- define "main" -}}
|
{{- define "main" -}}
|
||||||
{{- $params := .Scratch.Get "params" -}}
|
<div
|
||||||
{{- $currentPage := . -}}
|
class="container"
|
||||||
{{- with partial "function/currentMenuItem.html" . -}}
|
data-next="{{- i18n "next" -}}"
|
||||||
{{- partial "resources/imageJSON.html" (dict "Path" .DirName "Page" $currentPage) -}}
|
data-prev="{{- i18n "prev" -}}"
|
||||||
<script>
|
data-close="{{- i18n "close" -}}"
|
||||||
document.getElementById("main").setAttribute("currentMenuItemIndex", "{{- (sub .ID 1) -}}")
|
data-loading="{{- i18n "loading" -}}"
|
||||||
document.getElementById("main").setAttribute("nextText", "{{- i18n "next" -}}")
|
>
|
||||||
document.getElementById("main").setAttribute("prevText", "{{- i18n "prev" -}}")
|
{{- with .Content -}}
|
||||||
document.getElementById("main").setAttribute("closeText", "{{- i18n "close" -}}")
|
<article>
|
||||||
</script>
|
{{- . -}}
|
||||||
{{- end -}}
|
</article>
|
||||||
<div class="container">
|
{{- end -}}
|
||||||
|
|
||||||
{{- partial "nav.html" . -}}
|
{{- partial "nav.html" . -}}
|
||||||
</div>
|
</div>
|
||||||
<article class="info">
|
|
||||||
{{ .Content }}
|
|
||||||
</article>
|
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
{{- $Page := .Page -}}
|
{{- $context := . -}}
|
||||||
{{- $params := .Page.Params | merge .Site.Params.Page -}}
|
{{- $params := .Page.Params | merge .Site.Params.Page -}}
|
||||||
|
|
||||||
{{- $gallery := site.GetPage .Path -}}
|
{{- with partial "function/getImageSlice.html" . -}}
|
||||||
{{- with $gallery.Resources.ByType "image" -}}
|
|
||||||
{{- $index := len . -}}
|
{{- $index := len . -}}
|
||||||
{{- $Page.Scratch.Add "img" slice -}}
|
{{- $context.Scratch.Add "img" slice -}}
|
||||||
{{- range sort . "Name" "desc" -}}
|
{{- range sort . "Name" "desc" -}}
|
||||||
{{- $image := . -}}
|
{{- $image := . -}}
|
||||||
{{- $index = sub $index 1 -}}
|
{{- $index = sub $index 1 -}}
|
||||||
@@ -12,9 +11,9 @@
|
|||||||
{{- with $params.unifiedAlt -}}
|
{{- with $params.unifiedAlt -}}
|
||||||
{{- $alt = . -}}
|
{{- $alt = . -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- $lores := .Resize "800x webp Lanczos q60" -}}
|
{{- $lores := .Resize (site.Params.loResOpt | default "700x webp Lanczos q60") -}}
|
||||||
{{- $hires := .Resize "2500x webp Lanczos q75" -}}
|
{{- $hires := .Resize (site.Params.hiResOpt | default "2000x webp Lanczos q75") -}}
|
||||||
{{- $Page.Scratch.Add "img" (dict
|
{{- $context.Scratch.Add "img" (dict
|
||||||
"index" (int $index)
|
"index" (int $index)
|
||||||
"alt" (string $alt)
|
"alt" (string $alt)
|
||||||
"loUrl" (string $lores.RelPermalink)
|
"loUrl" (string $lores.RelPermalink)
|
||||||
@@ -26,5 +25,7 @@
|
|||||||
)
|
)
|
||||||
-}}
|
-}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
<script id="imagesSource" type="application/json">{{ $Page.Scratch.Get "img" | jsonify | safeJS }}</script>
|
{{ $context.Scratch.Get "img" | jsonify }}
|
||||||
|
{{- else -}}
|
||||||
|
[]
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
{{- $currentPage := . -}}
|
{{- $currentPage := . -}}
|
||||||
|
|
||||||
{{- $dirName := "" -}}
|
{{- $identifier := "" -}}
|
||||||
{{- $id := -1 -}}
|
{{- $title := "404" -}}
|
||||||
|
{{- $weight := -1 -}}
|
||||||
|
|
||||||
{{- range site.Menus.main -}}
|
{{- range site.Menus.main -}}
|
||||||
{{ $menu_item_url := .URL | relLangURL }}
|
{{ $menu_item_url := .URL | relLangURL }}
|
||||||
{{ $page_url:= $currentPage.RelPermalink | relLangURL }}
|
{{ $page_url:= $currentPage.RelPermalink | relLangURL }}
|
||||||
{{ if eq $menu_item_url $page_url }}
|
{{ if eq $menu_item_url $page_url }}
|
||||||
{{- $dirName = .Identifier -}}
|
{{- $identifier = .Identifier -}}
|
||||||
{{- $id = .Weight -}}
|
{{- $title = .Title -}}
|
||||||
|
{{- $weight = .Weight -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
{{- return (dict "DirName" $dirName "ID" $id) -}}
|
{{- return (dict "Identifier" $identifier "Title" $title "Weight" $weight) -}}
|
||||||
|
|||||||
10
layouts/partials/function/getImageSlice.html
Normal file
10
layouts/partials/function/getImageSlice.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{{- $context := . -}}
|
||||||
|
{{- $Path := "" -}}
|
||||||
|
{{- $params := .Page.Params | merge .Site.Params.Page -}}
|
||||||
|
|
||||||
|
{{- with partial "function/currentMenuItem.html" . -}}
|
||||||
|
{{- $Path = .Identifier -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- $gallery := site.GetPage $Path -}}
|
||||||
|
{{- return $gallery.Resources.ByType "image" -}}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
{{- partial "plugin/style.html" $style -}}
|
{{- partial "plugin/style.html" $style -}}
|
||||||
|
|
||||||
{{/* main style */}}
|
{{/* main style */}}
|
||||||
{{- if site.Params.bundled -}}
|
{{- if (site.Params.bundled | default true) -}}
|
||||||
{{- $style := dict "Link" "/bundled/css/style.min.css" "Defer" true -}}
|
{{- $style := dict "Link" "/bundled/css/style.min.css" "Defer" true -}}
|
||||||
{{- partial "plugin/style.html" $style -}}
|
{{- partial "plugin/style.html" $style -}}
|
||||||
{{- else -}}
|
{{- else -}}
|
||||||
@@ -18,15 +18,26 @@
|
|||||||
{{- partial "plugin/style.html" $style -}}
|
{{- partial "plugin/style.html" $style -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/* fuck safari */}}
|
||||||
|
<script>
|
||||||
|
function z() {
|
||||||
|
const r = document.querySelector(':root')
|
||||||
|
r.style.setProperty('--window-height', `${window.innerHeight}px`)
|
||||||
|
}
|
||||||
|
z()
|
||||||
|
window.addEventListener('resize', z, { passive: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
{{/* main js */}}
|
{{/* main js */}}
|
||||||
{{- $script := dict "Link" "/bundled/js/main.js" "Defer" true "Esm" true -}}
|
{{- $script := dict "Link" "/bundled/js/main.js" "Defer" true "Esm" true -}}
|
||||||
{{- partial "plugin/script.html" $script -}}
|
{{- partial "plugin/script.html" $script -}}
|
||||||
|
|
||||||
{{/* fonts */}}
|
{{/* fonts */}}
|
||||||
<link rel="preload" href="/lib/fonts/NotoSans-Regular.woff2" as="font" crossorigin />
|
<link rel="preload" href="/lib/fonts/fw.woff2" as="font" crossorigin />
|
||||||
{{- if (partial "function/langCode.html" (slice "en" "de" "fr" "es" "it")) -}}
|
{{- if (partial "function/langCode.html" (slice "en" "de" "fr" "es" "it")) -}}
|
||||||
<link rel="preload" href="/lib/fonts/GeistVF.woff2" as="font" crossorigin />
|
<link rel="preload" href="/lib/fonts/GeistVF.woff2" as="font" crossorigin />
|
||||||
{{- else if (partial "function/langCode.html" (slice "zh-cn" "zh-sg")) -}}
|
{{- else if (partial "function/langCode.html" (slice "zh-cn" "zh-sg")) -}}
|
||||||
|
<link rel="preload" href="/lib/fonts/NotoSans-Regular.woff2" as="font" crossorigin />
|
||||||
<link
|
<link
|
||||||
rel="preload"
|
rel="preload"
|
||||||
href="/lib/fonts/NotoSansCJKsc-Regular.woff2"
|
href="/lib/fonts/NotoSansCJKsc-Regular.woff2"
|
||||||
@@ -34,6 +45,7 @@
|
|||||||
crossorigin
|
crossorigin
|
||||||
/>
|
/>
|
||||||
{{- else if (partial "function/langCode.html" (slice "zh-tw" "zh-hk" "zh-mo")) -}}
|
{{- else if (partial "function/langCode.html" (slice "zh-tw" "zh-hk" "zh-mo")) -}}
|
||||||
|
<link rel="preload" href="/lib/fonts/NotoSans-Regular.woff2" as="font" crossorigin />
|
||||||
<link
|
<link
|
||||||
rel="preload"
|
rel="preload"
|
||||||
href="/lib/fonts/NotoSansCJKtc-Regular.woff2"
|
href="/lib/fonts/NotoSansCJKtc-Regular.woff2"
|
||||||
@@ -41,6 +53,7 @@
|
|||||||
crossorigin
|
crossorigin
|
||||||
/>
|
/>
|
||||||
{{- else if (partial "function/langCode.html" (slice "ja")) -}}
|
{{- else if (partial "function/langCode.html" (slice "ja")) -}}
|
||||||
|
<link rel="preload" href="/lib/fonts/NotoSans-Regular.woff2" as="font" crossorigin />
|
||||||
<link
|
<link
|
||||||
rel="preload"
|
rel="preload"
|
||||||
href="/lib/fonts/NotoSansCJKjp-Regular.woff2"
|
href="/lib/fonts/NotoSansCJKjp-Regular.woff2"
|
||||||
@@ -48,6 +61,7 @@
|
|||||||
crossorigin
|
crossorigin
|
||||||
/>
|
/>
|
||||||
{{- else if (partial "function/langCode.html" (slice "ko")) -}}
|
{{- else if (partial "function/langCode.html" (slice "ko")) -}}
|
||||||
|
<link rel="preload" href="/lib/fonts/NotoSans-Regular.woff2" as="font" crossorigin />
|
||||||
<link
|
<link
|
||||||
rel="preload"
|
rel="preload"
|
||||||
href="/lib/fonts/NotoSansCJKkr-Regular.woff2"
|
href="/lib/fonts/NotoSansCJKkr-Regular.woff2"
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<meta name="Description" content="{{- site.Params.description -}}" />
|
{{/* Title */}}
|
||||||
<meta
|
{{- $page_title := "" -}}
|
||||||
name="application-name"
|
{{- with partial "function/currentMenuItem.html" . -}}
|
||||||
content="{{- .Site.Params.app.title | default site.Title -}}"
|
{{ $page_title = printf "%s" site.Title | printf "%s%s" " | " | printf "%s%s" .Title | printf "%s" }}
|
||||||
/>
|
{{- end -}}
|
||||||
<meta
|
<title>{{ $page_title }}</title>
|
||||||
name="apple-mobile-web-app-title"
|
|
||||||
content="{{- .Site.Params.app.title | default site.Title -}}"
|
{{/* Basic */}}
|
||||||
/>
|
<meta name="Description" content="{{ site.Params.description }}" />
|
||||||
|
<meta name="application-name" content="{{ $page_title }}" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="{{ $page_title }}" />
|
||||||
|
|
||||||
|
{{/* Opengraph */}}
|
||||||
|
<meta property="og:title" content="{{ $page_title }}" />
|
||||||
|
<meta name="twitter:title" content="{{ $page_title }}" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="{{ .Permalink }}" />
|
||||||
|
<meta property="og:description" content="{{ site.Params.description }}" />
|
||||||
|
<meta name="twitter:description" content="{{ site.Params.description }}" />
|
||||||
|
|
||||||
|
{{/* Generator */}}
|
||||||
|
{{ hugo.Generator }}
|
||||||
|
|||||||
@@ -4,19 +4,39 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
{{- $index := 0 -}}
|
{{- $index := 0 -}}
|
||||||
|
{{- $currentIndex := -1 -}}
|
||||||
|
{{- with partial "function/currentMenuItem.html" . -}}
|
||||||
|
{{- $currentIndex = sub .Weight 1 -}}
|
||||||
|
{{- end -}}
|
||||||
{{- $menus := .Site.Menus.main -}}
|
{{- $menus := .Site.Menus.main -}}
|
||||||
{{- $len := len $menus }}
|
{{- $len := len $menus }}
|
||||||
{{- range $menus -}}
|
{{- range $menus -}}
|
||||||
{{- $url := .URL | relURL -}}
|
{{- $url := .URL | relURL -}}
|
||||||
{{- if eq (add $index 1) $len -}}
|
{{- if eq (add $index 1) $len -}}
|
||||||
<a href="{{- .URL | relURL -}}" class="link">{{- .Title -}}</a>
|
<a
|
||||||
|
href="{{- .URL | relURL -}}"
|
||||||
|
class="link{{- if eq $index $currentIndex -}}
|
||||||
|
{{- printf " current" -}}
|
||||||
|
{{- end -}}"
|
||||||
|
>{{- .Title -}}</a
|
||||||
|
>
|
||||||
{{- else -}}
|
{{- else -}}
|
||||||
<a href="{{- .URL | relURL -}}" class="link">{{- .Title -}}</a>, 
|
<a
|
||||||
|
href="{{- .URL | relURL -}}"
|
||||||
|
class="link{{- if eq $index $currentIndex -}}
|
||||||
|
{{- printf " current" -}}
|
||||||
|
{{- end -}}"
|
||||||
|
>{{- .Title -}}</a
|
||||||
|
>, 
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- $index = add $index 1 -}}
|
{{- $index = add $index 1 -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
<div class="threshold">
|
<div class="threshold">
|
||||||
|
{{- $length := 0 -}}
|
||||||
|
{{- with partial "function/getImageSlice.html" . -}}
|
||||||
|
{{- $length = len . -}}
|
||||||
|
{{- end -}}
|
||||||
<span>{{- i18n "threshold" | strings.FirstUpper -}}:</span>
|
<span>{{- i18n "threshold" | strings.FirstUpper -}}:</span>
|
||||||
<span>
|
<span>
|
||||||
<button class="dec">-</button>
|
<button class="dec">-</button>
|
||||||
@@ -26,10 +46,43 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="index">
|
<div class="index">
|
||||||
<span class="num"></span><span class="num"></span><span class="num"></span
|
<span class="num">0</span><span class="num">0</span><span class="num">0</span
|
||||||
><span class="num"></span>
|
><span class="num">0</span>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
<span class="num"></span><span class="num"></span><span class="num"></span
|
<span class="num">{{- math.Floor (div $length 1000) -}}</span
|
||||||
><span class="num"></span>
|
><span class="num">{{- mod (math.Floor (div $length 100)) 10 -}}</span
|
||||||
|
><span class="num">{{- mod (math.Floor (div $length 10)) 10 -}}</span
|
||||||
|
><span class="num">{{- mod $length 10 -}}</span>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
{{- /* for threshold */ -}}
|
||||||
|
<script>
|
||||||
|
i = 2
|
||||||
|
const s = sessionStorage.getItem('thresholdsIndex')
|
||||||
|
if (s !== null) {
|
||||||
|
i = parseInt(s)
|
||||||
|
}
|
||||||
|
var t = ''
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
t = '0020'
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
t = '0040'
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
t = '0080'
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
t = '0140'
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
t = '0200'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
Array.from(
|
||||||
|
document.getElementsByClassName('threshold').item(0).getElementsByClassName('num')
|
||||||
|
).forEach((e, i) => {
|
||||||
|
e.innerText = t[i]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|||||||
38
package.json
38
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bridget",
|
"name": "bridget",
|
||||||
"version": "0.0.1",
|
"version": "v1.0.0",
|
||||||
"description": "bridget theme source file",
|
"description": "bridget theme source file",
|
||||||
"packageManager": "pnpm@8.10.2",
|
"packageManager": "pnpm@8.10.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"lint": "eslint . --fix && prettier --write .",
|
"lint": "eslint . --fix && prettier --write .",
|
||||||
"lint:check": "eslint . && prettier . --check",
|
"lint:check": "eslint . && prettier . --check",
|
||||||
"dev": "run-p rollup:dev hugo:dev",
|
"dev": "run-p rollup:dev hugo:dev",
|
||||||
"build": "rm -f ./static/bundled/js/* && run-s rollup:build hugo:build && yes | cp -rf ./exampleSite/public/css/* ./static/bundled/css",
|
"build": "rm -f ./static/bundled/js/* && run-s rollup:build hugo:build && yes | cp -rf ./exampleSite/public/css/*.css ./static/bundled/css",
|
||||||
"server": "run-p rollup:server hugo:server",
|
"server": "run-p rollup:server hugo:server",
|
||||||
"rollup:build": "rollup -c --environment BUILD:production",
|
"rollup:build": "rollup -c --environment BUILD:production",
|
||||||
"rollup:server": "rollup -c --watch --environment BUILD:production",
|
"rollup:server": "rollup -c --watch --environment BUILD:production",
|
||||||
@@ -37,29 +37,29 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/Sped0n/bridget#readme",
|
"homepage": "https://github.com/Sped0n/bridget#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"@rollup/plugin-typescript": "^11.1.5",
|
"eslint": "^8.56.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.9.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"@typescript-eslint/parser": "^6.9.0",
|
|
||||||
"eslint": "^8.53.0",
|
|
||||||
"eslint-config-prettier": "^9.0.0",
|
|
||||||
"eslint-config-standard": "^17.1.0",
|
"eslint-config-standard": "^17.1.0",
|
||||||
"eslint-config-standard-with-typescript": "^39.1.1",
|
"eslint-config-standard-with-typescript": "^43.0.1",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-import": "^2.29.0",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-n": "^16.2.0",
|
"eslint-plugin-n": "^16.6.2",
|
||||||
"eslint-plugin-prettier": "^5.0.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "3.0.3",
|
"prettier": "3.2.5",
|
||||||
"prettier-plugin-go-template": "^0.0.15",
|
"prettier-plugin-go-template": "^0.0.15",
|
||||||
"prettier-plugin-organize-imports": "^3.2.3",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"rollup": "^4.3.0",
|
"typescript": "^5.3.3"
|
||||||
"typescript": "^5.2.2"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gsap": "^3.12.2",
|
"gsap": "^3.12.5",
|
||||||
"swiper": "^11.0.3"
|
"swiper": "^11.0.6",
|
||||||
|
"rollup": "^4.12.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
|
"@rollup/plugin-typescript": "^11.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
846
pnpm-lock.yaml
generated
846
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
2
static/bundled/css/style.min.css
vendored
2
static/bundled/css/style.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/bundled/js/CObFM1.js
Normal file
1
static/bundled/js/CObFM1.js
Normal file
File diff suppressed because one or more lines are too long
1
static/bundled/js/CUdXu0.js
Normal file
1
static/bundled/js/CUdXu0.js
Normal file
File diff suppressed because one or more lines are too long
1
static/bundled/js/DORuvs.js
Normal file
1
static/bundled/js/DORuvs.js
Normal file
File diff suppressed because one or more lines are too long
1
static/bundled/js/DrgyUb.js
Normal file
1
static/bundled/js/DrgyUb.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{W as e,s as t,n,f as a,l as s,g as o,h as i,r as c,j as r,c as d,i as l}from"./main.js";const p=new e(!1);function m(e,t){return Math.floor(Math.random()*(t-e+1))+e}let u,g,h,f,y,v,w,E=[],x=[],T=[],L=-1,b=!1;function k(){l.get()||(l.set(!0),T[t.get().index].scrollIntoView({block:"center",behavior:"auto"}),v.to(g,{y:"100%",ease:"power3.inOut",duration:1}),v.to(h,{opacity:0,duration:1.2,delay:.4}),setTimeout((()=>{a.set(!0),l.set(!1),L=-1}),1600))}let I=[];function B(e){o(e),!l.get()&&b&&(l.set(!0),v.to(h,{opacity:1,duration:1}),v.to(g,{y:0,ease:"power3.inOut",duration:1,delay:.4}),setTimeout((()=>{a.set(!1),l.set(!1)}),1400))}function N(e){(function(e){!function(e){const t=document.createElement("div");t.className="collection";for(const[n,a]of e.entries()){const e=0!==n?m(-25,25):0,s=0!==n?m(-30,30):0,o=document.createElement("img");o.dataset.src=a.loUrl,o.height=a.loImgH,o.width=a.loImgW,o.alt=a.alt,o.style.transform=`translate3d(${e}%, ${s-50}%, 0)`,t.append(o)}d.append(t)}(e);const t=document.getElementsByClassName("collection").item(0);p.addWatcher((e=>{e?t.classList.remove("hidden"):t.classList.add("hidden")})),I=Array.from(t.getElementsByTagName("img")),I.forEach(((e,t)=>{var n,a;t<5&&(e.src=e.dataset.src),e.addEventListener("click",(()=>{B(t)}),{passive:!0}),e.addEventListener("keydown",(()=>{B(t)}),{passive:!0}),n=e,a=e=>!(e.intersectionRatio<=0||(t+5<I.length&&(I[t+5].src=I[t+5].dataset.src),0)),new IntersectionObserver(((e,t)=>{for(const n of e)if(a(n)){t.disconnect();break}})).observe(n)}))})(e),function(e){!function(e){g=r("gallery"),function(e){const n=r("swiper-wrapper"),a=d.dataset.loading+"...";for(const s of e){const e=r("swiper-slide"),o=r("loadingText");o.innerText=a;const i=document.createElement("img");i.dataset.src=s.hiUrl,i.height=s.hiImgH,i.width=s.hiImgW,i.alt=s.alt,i.style.opacity="0",i.addEventListener("load",(()=>{t.get().index!==s.index?(v.set(i,{opacity:1}),v.set(o,{opacity:0})):(v.to(i,{opacity:1,delay:.5,duration:.5,ease:"power3.out"}),v.to(o,{opacity:0,duration:.5,ease:"power3.in"}))}),{once:!0,passive:!0});const c=r("slideContainer");c.append(i,o),e.append(c),n.append(e)}u=r("galleryInner"),u.append(n)}(e),function(){f=document.createElement("div"),f.insertAdjacentHTML("afterbegin",'<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>\n <span>/</span>\n <span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>');const e=document.createElement("div");var t;e.innerText=(t=d.dataset.close).charAt(0).toUpperCase()+t.slice(1),e.addEventListener("click",(()=>{k()}),{passive:!0}),e.addEventListener("keydown",(()=>{k()}),{passive:!0}),y=r("nav"),y.append(f,e)}(),g.append(u,y),h=r("curtain"),d.append(g,h)}(e),E=Array.from(f.getElementsByClassName("num")??[]),x=Array.from(g.getElementsByTagName("img")),T=Array.from(document.getElementsByClassName("collection").item(0)?.getElementsByTagName("img")??[]),t.addWatcher((e=>{var a;e.index!==L&&(-1===L?n.set("none"):e.index<L?n.set("prev"):e.index>L?n.set("next"):n.set("none"),a=e.index,function(){let e=[];const a=t.get().index,s=Math.min(a+1,t.get().length-1),o=Math.max(a-1,0);switch(n.get()){case"next":e=[s];break;case"prev":e=[o];break;case"none":e=[a,s,o]}c(e).forEach((e=>{const t=x[e];t.src!==t.dataset.src&&(t.src=t.dataset.src)}))}(),w.slideTo(a,0),function(){const e=i(t.get().index+1),n=i(t.get().length);E.forEach(((t,a)=>{t.innerText=a<4?e[a]:n[a-4]}))}(),L=e.index)})),p.addWatcher((e=>{e&&a.set(!0)})),window.addEventListener("touchstart",(()=>{s().then((e=>{v=e})).catch((e=>{console.log(e)})),async function(){return(await import("./CUdXu0.js")).Swiper}().then((e=>{w=new e(u,{spaceBetween:20}),w.on("slideChange",(({realIndex:e})=>{o(e)}))})).catch((e=>{console.log(e)})),b=!0}),{once:!0,passive:!0}),p.set(!0)}(e)}export{N as initMobile};
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
import{W as e,s as t,e as n,l as a,f as s,g as c,h as o,j as l,c as m,o as i,k as d}from"./main.js";const r=new e(!1);let p,u,g,h;const E=new e(!1);let y,v,f,N=-1,w=[],x=[],I=[],B=!1;function T(){n.set(!0),I[t.get().index].scrollIntoView({block:"center",behavior:"auto"}),v.to(u,{y:"100%",ease:f.easeInOut,duration:1}),v.to(g,{opacity:0,duration:1.2,delay:.4})}function C(){const e=[];e.push(x[h.activeIndex]),e.push(x[Math.min(h.activeIndex+1,h.slides.length)]),e.push(x[Math.max(h.activeIndex-1,0)]);for(const t of e)t.src=t.dataset.src}let L=[];function k(e){c(e),!E.get()&&B&&(E.set(!0),C(),v.to(g,{opacity:1,duration:1}),v.to(u,{y:0,ease:f.easeInOut,duration:1,delay:.4}),setTimeout((()=>{n.set(!1),E.set(!1)}),1200))}function A(e){(function(e){!function(e){const t=document.createElement("div");t.className="collection";for(const[n,a]of e.entries()){const e=0!==n?d(-25,25):0,s=0!==n?d(-30,30):0,c=document.createElement("img");c.dataset.src=a.loUrl,c.height=a.loImgH,c.width=a.loImgW,c.alt=a.alt,c.style.transform=`translate3d(${e}%, ${s-50}%, 0)`,t.append(c)}m.append(t)}(e);const t=document.getElementsByClassName("collection").item(0);r.addWatcher((e=>{e?t.classList.remove("hidden"):t.classList.add("hidden")})),L=Array.from(t.getElementsByTagName("img")),L.forEach(((e,t)=>{e.addEventListener("click",(()=>{k(t)}),{passive:!0}),e.addEventListener("keydown",(()=>{k(t)}),{passive:!0}),i(e,(()=>{for(let e=0;e<5;e++){const n=t+e;n<0||n>L.length-1||(L[n].src=L[n].dataset.src)}}))}))})(e),function(e){!function(e){const t=document.createElement("div");t.className="swiper-wrapper";for(const n of e){const e=document.createElement("div");e.className="swiper-slide";const a=document.createElement("img");a.dataset.src=n.hiUrl,a.height=n.hiImgH,a.width=n.hiImgW,a.alt=n.alt,e.append(a),t.append(e)}const n=document.createElement("div");n.className="galleryInner",n.append(t);const a=document.createElement("div");a.insertAdjacentHTML("afterbegin",'<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>\n <span>/</span>\n <span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>');const s=document.createElement("div"),c=document.getElementById("main")?.getAttribute("closeText");s.innerText=l(c),s.addEventListener("click",(()=>{T()}),{passive:!0}),s.addEventListener("keydown",(()=>{T()}),{passive:!0});const o=document.createElement("div");o.className="nav",o.append(a,s);const i=document.createElement("div");i.className="gallery",i.append(n),i.append(o);const d=document.createElement("div");d.className="curtain",m.append(i,d)}(e),w=Array.from(document.getElementsByClassName("nav").item(0)?.getElementsByClassName("num")??[]),p=document.getElementsByClassName("galleryInner").item(0),u=document.getElementsByClassName("gallery").item(0),g=document.getElementsByClassName("curtain").item(0),x=Array.from(u.getElementsByTagName("img")),I=Array.from(document.getElementsByClassName("collection").item(0)?.getElementsByTagName("img")??[]),t.addWatcher((()=>{const e=t.get();var n;e.index!==N&&(n=e.index,C(),h.slideTo(n,0),function(){const e=o(t.get().index+1),n=o(t.get().length);w.forEach(((t,a)=>{t.innerText=a<4?e[a]:n[a-4]}))}(),N=e.index)})),r.addWatcher((e=>{e&&n.set(!0)})),window.addEventListener("touchstart",(()=>{a().then((e=>{v=e[0],f=e[1]})).catch((e=>{console.log(e)})),s().then((e=>{y=e,h=new y(p,{spaceBetween:20}),h.on("slideChange",(({realIndex:e})=>{c(e)}))})).catch((e=>{console.log(e)})),B=!0}),{once:!0,passive:!0}),r.set(!0)}(e)}export{A as initMobile};
|
|
||||||
@@ -1 +1 @@
|
|||||||
function t(t,e){return(t+1)%e}function e(t,e){return(t+e-1)%e}function n(t){return("0000"+t.toString()).slice(-4)}function s(t,e){return Math.floor(Math.random()*(e-t+1))+t}function o(t,e){new IntersectionObserver(((n,s)=>{n.forEach((n=>{n.intersectionRatio>0&&(e(t),s.disconnect())}))})).observe(t)}function r(t){return t.charAt(0).toUpperCase()+t.slice(1)}async function i(){const t=await import("./-BhIIs.js");return[t.gsap,t.Power3]}async function a(){return(await import("./ep7_9p.js")).Swiper}class c{constructor(t){this.obj=t,this.watchers=[]}get(){return this.obj}set(t){this.obj=t,this.watchers.forEach((t=>{t(this.obj)}))}addWatcher(t){this.watchers.push(t)}}const l=new c(!0);let h;const d=[{threshold:20,trailLength:20},{threshold:40,trailLength:10},{threshold:80,trailLength:5},{threshold:140,trailLength:5},{threshold:200,trailLength:5}],m=new c({index:-1,length:0,threshold:d[E()].threshold,trailLength:d[E()].trailLength});function u(t){const e=m.get();e.index=t,m.set(e)}function g(){const e=m.get();e.index=t(e.index,e.length),m.set(e)}function f(){const t=m.get();t.index=e(t.index,t.length),m.set(t)}function x(t,e){const n=d.findIndex((e=>t.threshold===e.threshold))+e;if(n<0||n>=d.length)return t;sessionStorage.setItem("thresholdsIndex",n.toString());const s=d[n];return{...t,...s}}function E(){const t=sessionStorage.getItem("thresholdsIndex");return null===t?2:parseInt(t)}const p=document.getElementsByClassName("threshold").item(0),y=Array.from(p.getElementsByClassName("num")),w=p.getElementsByClassName("dec").item(0),I=p.getElementsByClassName("inc").item(0),L=document.getElementsByClassName("index").item(0),b=Array.from(L.getElementsByClassName("num")),B=document.getElementsByClassName("links").item(0),C=Array.from(B.getElementsByClassName("link")),N=document.getElementById("main")?.getAttribute("currentMenuItemIndex");for(const[t,e]of C.entries())t===parseInt(N)&&(e.classList.add("current"),0!==t&&(document.title=e.innerText+" | "+document.title));function j(t){y.forEach(((e,n)=>{e.innerText=t[n]}))}function S(t,e){b.forEach(((n,s)=>{n.innerText=s<4?t[s]:e[s-4]}))}h=document.getElementsByClassName("container").item(0),l.addWatcher((t=>{t?h.classList.remove("disableScroll"):h.classList.add("disableScroll")}));const v=function(){const t=document.getElementById("imagesSource");return null===t?[]:JSON.parse(t.textContent).sort(((t,e)=>t.index<e.index?-1:1))}();!function(t){const e=m.get();e.length=t,x(e,0),m.set(e)}(v.length),function(){const t=m.get();j(n(t.threshold)),S(n(t.index+1),n(t.length)),m.addWatcher((t=>{S(n(t.index+1),n(t.length)),j(n(t.threshold))})),w.addEventListener("click",(()=>{!function(){let t=m.get();t=x(t,-1),m.set(t)}()}),{passive:!0}),I.addEventListener("click",(()=>{!function(){let t=m.get();t=x(t,1),m.set(t)}()}),{passive:!0})}(),v.length>0&&(window.matchMedia("(hover: none)").matches?import("./fRwBqI.js").then((t=>{t.initMobile(v)})).catch((t=>{console.log(t)})):import("./wWaJYt.js").then((t=>{t.initDesktop(v)})).catch((t=>{console.log(t)})));export{c as W,g as a,f as b,h as c,e as d,l as e,a as f,u as g,n as h,t as i,r as j,s as k,i as l,o,m as s};
|
function t(t,e){return(t+1)%e}function e(t,e){return(t+e-1)%e}function n(t){return("0000"+t.toString()).slice(-4)}async function s(){return(await import("./DORuvs.js")).gsap}function a(){const t=sessionStorage.getItem("thresholdsIndex");return null===t?2:parseInt(t)}function o(t){return t.length<2?t:[...new Set(t)]}function i(t){const e=document.createElement("div");return""===t||e.classList.add(t),e}class r{constructor(t,e=!0){this.obj=t,this.lazy=e,this.watchers=[]}get(){return this.obj}set(t){t===this.obj&&this.lazy||(this.obj=t,this.watchers.forEach((t=>{t(this.obj)})))}addWatcher(t){this.watchers.push(t)}}const c=new r(!0);let l;const h=[{threshold:20,trailLength:20},{threshold:40,trailLength:10},{threshold:80,trailLength:5},{threshold:140,trailLength:5},{threshold:200,trailLength:5}],d=new r({index:-1,length:0,threshold:h[a()].threshold,trailLength:h[a()].trailLength},!1),g=new r(!1),u=new r("none");function m(t){const e=d.get();e.index=t,d.set(e)}function f(){const e=d.get();e.index=t(e.index,e.length),d.set(e)}function w(){const t=d.get();t.index=e(t.index,t.length),d.set(t)}function x(t,e){const n=h.findIndex((e=>t.threshold===e.threshold))+e;if(n<0||n>=h.length)return t;sessionStorage.setItem("thresholdsIndex",n.toString());const s=h[n];return{...t,...s}}const y=document.getElementsByClassName("threshold").item(0),p=Array.from(y.getElementsByClassName("num")),E=y.getElementsByClassName("dec").item(0),L=y.getElementsByClassName("inc").item(0),j=document.getElementsByClassName("index").item(0),b=Array.from(j.getElementsByClassName("num"));document.addEventListener("DOMContentLoaded",(()=>{(async function(){l=document.getElementsByClassName("container").item(0),c.addWatcher((t=>{t?l.classList.remove("disableScroll"):l.classList.add("disableScroll")}));const t=await async function(){if("404"===document.title.split(" | ")[0])return[];try{const t=await fetch(`${window.location.href}index.json`,{headers:{Accept:"application/json"}});return(await t.json()).sort(((t,e)=>t.index<e.index?-1:1))}catch(t){return[]}}();(function(t){const e=d.get();e.length=t,x(e,0),d.set(e)})(t.length),d.addWatcher((t=>{var e,s,a;e=n(t.index+1),s=n(t.length),b.forEach(((t,n)=>{t.innerText=n<4?e[n]:s[n-4]})),a=n(t.threshold),p.forEach(((t,e)=>{t.innerText=a[e]}))})),E.addEventListener("click",(()=>{!function(){let t=d.get();t=x(t,-1),d.set(t)}()}),{passive:!0}),L.addEventListener("click",(()=>{!function(){let t=d.get();t=x(t,1),d.set(t)}()}),{passive:!0}),0!==t.length&&(window.matchMedia("(hover: none)").matches?await import("./DrgyUb.js").then((e=>{e.initMobile(t)})).catch((t=>{console.log(t)})):await import("./CObFM1.js").then((e=>{e.initDesktop(t)})).catch((t=>{console.log(t)})))})().catch((t=>{console.log(t)}))}));export{r as W,f as a,t as b,l as c,e as d,w as e,c as f,m as g,n as h,g as i,i as j,s as l,u as n,o as r,d as s};
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{l as e,c as t,W as n,s as a,i as s,a as i,d as o,b as c}from"./main.js";let r=[],d={x:0,y:0};const g=new n([]),l=new n(!1),m=new n(!1),u=new n(!1);let h,p,f=!1;function v(){return g.get().map((e=>r[e.i]))}function y(){const e=v().slice(-a.get().trailLength);return e.slice(0,e.length-1)}function w(){const e=v();return e[e.length-1]}function I(){const e=a.get(),t=[];for(let n=0;n<5;n++)t.push(r[s(e.index+n,e.length)]);return t}function x(){const e=a.get();return r[s(e.index,e.length)]}function E(){const e=a.get();return r[o(e.index,e.length)]}function L(e){if(l.get()||m.get()||!f)return;const t={x:e.clientX,y:e.clientY};if(Math.hypot(t.x-d.x,t.y-d.y)>a.get().threshold){d=t,i();const e={i:a.get().index,...t};g.set([...g.get(),e].slice(-a.get().length))}}function W(){if(m.get()||!f)return;l.set(!0),m.set(!0),k([w(),x(),E()]);const e=h.timeline();e.to(y(),{y:"+=20",ease:p.easeIn,stagger:.075,duration:.3,delay:.1,opacity:0}),e.to(w(),{x:0,y:0,ease:p.easeInOut,duration:.7,delay:.3}),e.to(w(),{delay:.1,scale:1,ease:p.easeInOut}),e.then((()=>{m.set(!1)})).catch((e=>{console.log(e)}))}function H(){if(m.get()||!f)return;l.set(!1),m.set(!0),N([w()]),N(y());const e=h.timeline();e.to(w(),{scale:.6,duration:.6,ease:p.easeInOut}),e.to(w(),{delay:.3,duration:.7,ease:p.easeInOut,x:g.get()[g.get().length-1].x-window.innerWidth/2,y:g.get()[g.get().length-1].y-window.innerHeight/2}),e.to(y(),{y:"-=20",ease:p.easeOut,stagger:-.1,duration:.3,opacity:1}),e.then((()=>{m.set(!1)})).catch((e=>{console.log(e)}))}function k(e){e.forEach((e=>{e.src=e.dataset.hiUrl,e.height=parseInt(e.dataset.hiImgH),e.width=parseInt(e.dataset.hiImgW)}))}function N(e){e.forEach((e=>{e.src=e.dataset.loUrl,e.height=parseInt(e.dataset.loImgH),e.width=parseInt(e.dataset.loImgW)}))}const b=document.createElement("div"),A=document.createElement("div");function O(e){const t=e.clientX,n=e.clientY;b.style.transform=`translate3d(${t}px, ${n}px, 0)`}function U(e){A.innerText=e}const T=document.getElementById("main"),S=[T.getAttribute("prevText"),T.getAttribute("closeText"),T.getAttribute("nextText")];function B(e){e===S[0]?$():e===S[1]?H():Y()}function X(e){if(!l.get()&&!m.get())switch(e.key){case"ArrowLeft":$();break;case"Escape":H();break;case"ArrowRight":Y()}}function Y(){m.get()||(g.set(g.get().map((e=>({...e,i:s(e.i,a.get().length)})))),i())}function $(){m.get()||(g.set(g.get().map((e=>({...e,i:o(e.i,a.get().length)})))),c())}function j(n){b.className="cursor",A.className="cursorInner",b.append(A),t.append(b),window.addEventListener("mousemove",O,{passive:!0}),u.addWatcher((e=>{e?b.classList.add("active"):b.classList.remove("active")})),function(n){!function(e){const n=document.createElement("div");n.className="stage";for(const t of e){const e=document.createElement("img");e.height=t.loImgH,e.width=t.loImgW,e.dataset.hiUrl=t.hiUrl,e.dataset.hiImgH=t.hiImgH.toString(),e.dataset.hiImgW=t.hiImgW.toString(),e.dataset.loUrl=t.loUrl,e.dataset.loImgH=t.loImgH.toString(),e.dataset.loImgW=t.loImgW.toString(),e.alt=t.alt,n.append(e)}t.append(n)}(n);const s=document.getElementsByClassName("stage").item(0);r=Array.from(s.getElementsByTagName("img")),s.addEventListener("click",(()=>{W()})),s.addEventListener("keydown",(()=>{W()})),window.addEventListener("mousemove",L,{passive:!0}),l.addWatcher((e=>{u.set(e&&!m.get())})),m.addWatcher((e=>{u.set(l.get()&&!e)})),g.addWatcher((e=>{!function(){const e=v();0!==e.length&&f&&(N(I()),h.set(e,{x:e=>g.get()[e].x-window.innerWidth/2,y:e=>g.get()[e].y-window.innerHeight/2,opacity:e=>e+1+a.get().trailLength<=g.get().length?0:1,zIndex:e=>e,scale:.6}),l.get()&&(N(v()),k([w()]),h.set(r,{opacity:0}),h.set(w(),{opacity:1,x:0,y:0,scale:1})))}()})),N(I()),window.addEventListener("mousemove",(()=>{e().then((e=>{h=e[0],p=e[1],f=!0})).catch((e=>{console.log(e)}))}),{once:!0,passive:!0})}(n),function(){const e=document.createElement("div");e.className="navOverlay";for(const t of S){const n=document.createElement("div");n.className="overlay",n.addEventListener("click",(()=>{B(t)}),{passive:!0}),n.addEventListener("keydown",(()=>{B(t)}),{passive:!0}),n.addEventListener("mouseover",(()=>{U(t)}),{passive:!0}),n.addEventListener("focus",(()=>{U(t)}),{passive:!0}),e.append(n)}u.addWatcher((()=>{u.get()?e.classList.add("active"):e.classList.remove("active")})),t.append(e),window.addEventListener("keydown",X,{passive:!0})}()}export{j as initDesktop};
|
|
||||||
Binary file not shown.
BIN
static/lib/fonts/fw.woff2
Normal file
BIN
static/lib/fonts/fw.woff2
Normal file
Binary file not shown.
@@ -7,7 +7,7 @@ licenselink = "https://github.com/Sped0n/bridget/blob/main/LICENSE"
|
|||||||
description = "Bridget is a minimal Hugo theme designed for photographers/visual artists."
|
description = "Bridget is a minimal Hugo theme designed for photographers/visual artists."
|
||||||
homepage = "https://github.com/Sped0n/bridget"
|
homepage = "https://github.com/Sped0n/bridget"
|
||||||
demosite = "https://bridget-demo.sped0nwen.com"
|
demosite = "https://bridget-demo.sped0nwen.com"
|
||||||
tags = ["photography", "visual art", "minimal", "portfolio", "responsive"]
|
tags = ["gallery", "minimal", "portfolio", "responsive"]
|
||||||
features = ["SEO optimized", "TypeScript", "multi-language support"]
|
features = ["SEO optimized", "TypeScript", "multi-language support"]
|
||||||
|
|
||||||
[author]
|
[author]
|
||||||
|
|||||||
Reference in New Issue
Block a user