From 211e2fd4fb4ab7a293c503c70078095b6ef5a55f Mon Sep 17 00:00:00 2001 From: Spedon Date: Tue, 14 Mar 2023 01:01:23 +0800 Subject: [PATCH] transition from javascript to typescript --- assets/js/theme.js | 233 ------------------------------------- assets/ts/indexDisp.ts | 17 +++ assets/ts/main.ts | 13 +++ assets/ts/overlay.ts | 85 ++++++++++++++ assets/ts/thresholdCtl.ts | 40 +++++++ assets/ts/trackMouse.ts | 107 +++++++++++++++++ assets/ts/utils.ts | 78 +++++++++++++ layouts/index.html | 2 +- layouts/partials/head.html | 4 +- 9 files changed, 343 insertions(+), 236 deletions(-) delete mode 100644 assets/js/theme.js create mode 100644 assets/ts/indexDisp.ts create mode 100644 assets/ts/main.ts create mode 100644 assets/ts/overlay.ts create mode 100644 assets/ts/thresholdCtl.ts create mode 100644 assets/ts/trackMouse.ts create mode 100644 assets/ts/utils.ts diff --git a/assets/js/theme.js b/assets/js/theme.js deleted file mode 100644 index 85703be..0000000 --- a/assets/js/theme.js +++ /dev/null @@ -1,233 +0,0 @@ -const imagesArray = JSON.parse(document.getElementById('images_array').textContent).sort((a, b) => { - if (a.index < b.index) { - return -1 - } - return 1 -}) - -const imagesArrayLen = imagesArray.length - -const thresholdNum = document.getElementsByClassName('thid') - -const threshold = [0, 40, 80, 120, 160, 200] - -const thresholdSensitivity = [100, 40, 18, 14, 9, 5] - -const r = document.querySelector(':root') - -const posXArray = ['0px', '0px', '0px', '0px', '0px'] - -const posYArray = ['0px', '0px', '0px', '0px', '0px'] - -const layer5 = document.getElementById('layer5') -const layer4 = document.getElementById('layer4') -const layer3 = document.getElementById('layer3') -const layer2 = document.getElementById('layer2') -const layer1 = document.getElementById('layer1') - -let thresholdIndex = 2 - -let globalIndex = 0 - -let last = { x: 0, y: 0 } - -function sleep (ms) { - return new Promise(resolve => setTimeout(resolve, ms)) -} - -// fulfill space with zero -function duper (num) { - return ('0000' + num).slice(-4) -} - -// threshold display update -function thresholdUpdate () { - thresholdNum.item(0).innerText = duper(threshold[thresholdIndex]) -} - -function footerHeightUpdate () { - if (window.innerWidth > 768) { - r.style.setProperty('--footer-height', '38px') - } else { - r.style.setProperty('--footer-height', '31px') - } -} - -function overlayInit () { - // largest z index - document.getElementsByClassName('overlay').item(0).style.zIndex = '7' -} - -const numSpan = (numOne, numTwo) => { - // footer index number display module - const footerIndex = document.getElementsByClassName('ftid') - const numOneString = duper(numOne) - const numTwoString = duper(numTwo) - for (let i = 0; i <= 7; i++) { - if (i > 3) { - footerIndex.item(i).innerText = numTwoString[i - 4] - } else { - footerIndex.item(i).innerText = numOneString[i] - } - } -} - -// initialization -function init () { - numSpan(0, imagesArrayLen) - thresholdUpdate() - footerHeightUpdate() - // threshold buttons initialization - document.getElementById('thresholdDec').addEventListener('click', function () { - if (thresholdIndex > 0) { - thresholdIndex-- - thresholdUpdate() - } - }, { passive: true }) - - document.getElementById('thresholdInc').addEventListener('click', function () { - if (thresholdIndex < 5) { - thresholdIndex++ - thresholdUpdate() - } - }, { passive: true }) -} - -const center = e => { - e.style.left = '50%' - if (window.innerWidth > 768) { - e.style.top = 'calc((100% - 38px) / 2)' - } else { - e.style.top = 'calc((100% - 31px) / 2 + 31px)' - } -} - -const overlayCursor = e => { - const overlayCursor = document.getElementsByClassName('overlay_cursor').item(0) - overlayCursor.style.left = `${e.clientX}px` - overlayCursor.style.top = `${e.clientY}px` -} - -const FIFO = element => { - function layerProcess (layerL, layerH) { - if (layerL.childElementCount) layerL.removeChild(layerL.lastElementChild) - if (layerH.childElementCount) layerL.appendChild(layerH.lastElementChild.cloneNode(true)) - } - layerProcess(layer1, layer2) - layerProcess(layer2, layer3) - layerProcess(layer3, layer4) - layerProcess(layer4, layer5) - if (layer5.childElementCount) layer5.removeChild(layer5.lastElementChild) - layer5.appendChild(element) -} - -const posCache = (x, y) => { - // pop element if length surpass limitation - posXArray.shift() - posYArray.shift() - // push new element - posXArray.push(`${x}px`) - posYArray.push(`${y}px`) -} - -function layersPosSet () { - function posSet (layer, index) { - layer.style.left = posXArray[index] - layer.style.top = posYArray[index] - } - posSet(layer5, 4) - posSet(layer4, 3) - posSet(layer3, 2) - posSet(layer2, 1) - posSet(layer1, 0) -} - -// let specified image show -const activate = (index, x, y) => { - const img = document.createElement('img') - img.setAttribute('src', imagesArray[index].url) - img.setAttribute('alt', imagesArray[index].index) - img.setAttribute('height', imagesArray[index].height) - img.setAttribute('width', imagesArray[index].width) - posCache(x, y) - layersPosSet() - FIFO(img) - // top - layer5.addEventListener('click', () => { - // stop images animation - window.removeEventListener('mousemove', handleOnMove) - // set top image - center(layer5) - layer5.dataset.status = 't0' - layer4.dataset.status = 't1' - layer3.dataset.status = 't2' - layer2.dataset.status = 't3' - layer1.dataset.status = 't4' - // overlay init - overlayInit() - window.addEventListener('mousemove', overlayCursor) - }, { - passive: true, - once: true - }) - - last = { x, y } -} - -// absolute distance calculation -const distanceFromLast = (x, y) => { - return Math.hypot(x - last.x, y - last.y) -} - -// move handler -const handleOnMove = e => { - if (distanceFromLast(e.clientX, e.clientY) > (window.innerWidth / thresholdSensitivity[thresholdIndex])) { - // images showing array - const imageIndex = globalIndex % imagesArrayLen - // show top image and change index - activate(imageIndex, e.clientX, e.clientY) - numSpan((imageIndex + 1), imagesArrayLen) - // self increment - globalIndex++ - } -} - -init() - -window.addEventListener('mousemove', handleOnMove) -window.addEventListener('resize', () => { - const isTop = document.getElementsByClassName('top') - if (isTop.length) { - center(isTop.item(0)) - } - footerHeightUpdate() -}) - -document.getElementsByClassName('prev_section').item(0).addEventListener('mouseover', () => { - document.getElementsByClassName('overlay_cursor').item(0).innerText = 'PREV' -}) - -document.getElementsByClassName('close_section').item(0).addEventListener('click', async function f () { - document.getElementsByClassName('overlay').item(0).style.zIndex = '-1' - layersPosSet() - layer5.dataset.status = 'r0' - layer4.dataset.status = 'r1' - layer3.dataset.status = 'r2' - layer2.dataset.status = 'r3' - layer1.dataset.status = 'r4' - await sleep(2500) - layer5.dataset.status = 'null' - layer4.dataset.status = 'null' - layer3.dataset.status = 'null' - layer2.dataset.status = 'null' - layer1.dataset.status = 'null' - window.addEventListener('mousemove', handleOnMove) -}) - -document.getElementsByClassName('close_section').item(0).addEventListener('mouseover', () => { - document.getElementsByClassName('overlay_cursor').item(0).innerText = 'CLOSE' -}) - -document.getElementsByClassName('next_section').item(0).addEventListener('mouseover', () => { - document.getElementsByClassName('overlay_cursor').item(0).innerText = 'NEXT' -}) diff --git a/assets/ts/indexDisp.ts b/assets/ts/indexDisp.ts new file mode 100644 index 0000000..0ef49f9 --- /dev/null +++ b/assets/ts/indexDisp.ts @@ -0,0 +1,17 @@ +import { duper } from './utils' + +// update index of displaying image +export function imgIndexSpanUpdate(numOne: number, numTwo: number): void { + // footer index number display module + const footerIndexDisp = document.getElementsByClassName('ftid') + const numOneString: string = duper(numOne) + const numTwoString: string = duper(numTwo) + for (let i: number = 0; i <= 7; i++) { + const footerIndex = footerIndexDisp[i] as HTMLSpanElement + if (i > 3) { + footerIndex.innerText = numTwoString[i - 4] + } else { + footerIndex.innerText = numOneString[i] + } + } +} diff --git a/assets/ts/main.ts b/assets/ts/main.ts new file mode 100644 index 0000000..1d5297d --- /dev/null +++ b/assets/ts/main.ts @@ -0,0 +1,13 @@ +import { footerHeightUpdateInit } from './utils' +import { imgIndexSpanUpdate } from './indexDisp' +import { imagesArrayLen, trackMouseInit } from './trackMouse' +import { thresholdCtlInit } from './thresholdCtl' + +function init(): void { + footerHeightUpdateInit() + imgIndexSpanUpdate(0, imagesArrayLen) + thresholdCtlInit() + trackMouseInit() +} + +init() diff --git a/assets/ts/overlay.ts b/assets/ts/overlay.ts new file mode 100644 index 0000000..c3a14e1 --- /dev/null +++ b/assets/ts/overlay.ts @@ -0,0 +1,85 @@ +import { delay, removeAllEventListeners, layersPosSet } from './utils' +import { posArray, layers, handleOnMove } from './trackMouse' + +// get components of overlay +const overlay = document.getElementsByClassName('overlay').item(0) as HTMLDivElement +let closeSection = document.getElementsByClassName('close_section').item(0) as Node +let prevSection = document.getElementsByClassName('prev_section').item(0) as Node +let nextSection = document.getElementsByClassName('next_section').item(0) as Node +const cursor = document + .getElementsByClassName('overlay_cursor') + .item(0) as HTMLDivElement + +// set cursor text +function setCursorText(text: string): void { + cursor.innerText = text +} + +// overlay cursor event handler +const overlayCursor = (e: MouseEvent): void => { + cursor.style.left = `${e.clientX}px` + cursor.style.top = `${e.clientY}px` +} + +function disableListener(): void { + window.removeEventListener('mousemove', overlayCursor) + closeSection = removeAllEventListeners(closeSection) + prevSection = removeAllEventListeners(prevSection) + nextSection = removeAllEventListeners(nextSection) +} + +export function overlayEnable(): void { + overlay.style.zIndex = '7' + setListener() +} + +export function overlayDisable(): void { + overlay.style.zIndex = '-1' + disableListener() +} + +async function handleCloseClick(): Promise { + overlayDisable() + layersPosSet(posArray, layers) + for (let i: number = 4; i >= 0; i--) { + layers[i].dataset.status = `r${4 - i}` + } + await delay(2500) + for (let i: number = 4; i >= 0; i--) { + layers[i].dataset.status = 'null' + } + window.addEventListener('mousemove', handleOnMove) +} + +// set hover event listener +function setListener(): void { + window.addEventListener('mousemove', overlayCursor, { passive: true }) + closeSection.addEventListener( + 'mouseover', + () => { + setCursorText('CLOSE') + }, + { passive: true } + ) + closeSection.addEventListener( + 'click', + () => { + void handleCloseClick() + }, + { passive: true } + ) + prevSection.addEventListener( + 'mouseover', + () => { + setCursorText('PREV') + }, + { passive: true } + ) + nextSection.addEventListener( + 'mouseover', + () => { + setCursorText('NEXT') + }, + { passive: true } + ) +} diff --git a/assets/ts/thresholdCtl.ts b/assets/ts/thresholdCtl.ts new file mode 100644 index 0000000..e674fcb --- /dev/null +++ b/assets/ts/thresholdCtl.ts @@ -0,0 +1,40 @@ +import { duper } from './utils' + +// get threshold display element +const thresholdDisp = document.getElementsByClassName('thid').item(0) as HTMLSpanElement + +// threshold data +const threshold: number[] = [0, 40, 80, 120, 160, 200] +export const thresholdSensitivityArray: number[] = [100, 40, 18, 14, 9, 5] +export let thresholdIndex: number = 2 + +function thresholdUpdate(): void { + thresholdDisp.innerText = duper(threshold[thresholdIndex]) +} + +export function thresholdCtlInit(): void { + thresholdUpdate() + const dec = document.getElementById('thresholdDec') as HTMLButtonElement + dec.addEventListener( + 'click', + function () { + if (thresholdIndex > 0) { + thresholdIndex-- + thresholdUpdate() + } + }, + { passive: true } + ) + + const inc = document.getElementById('thresholdInc') as HTMLButtonElement + inc.addEventListener( + 'click', + function () { + if (thresholdIndex < 5) { + thresholdIndex++ + thresholdUpdate() + } + }, + { passive: true } + ) +} diff --git a/assets/ts/trackMouse.ts b/assets/ts/trackMouse.ts new file mode 100644 index 0000000..ca29c53 --- /dev/null +++ b/assets/ts/trackMouse.ts @@ -0,0 +1,107 @@ +import { overlayEnable } from './overlay' +import { posCache, FIFO, layersPosSet, center } from './utils' +import { thresholdSensitivityArray, thresholdIndex } from './thresholdCtl' +import { imgIndexSpanUpdate } from './indexDisp' + +interface ImageData { + index: string + url: string + imgH: string + imgW: string +} + +export interface position { + x: number + y: number +} + +// get images info from JSON +const imageArrayElement = document.getElementById('images_array') as HTMLElement +const rawImageArray = imageArrayElement.textContent as string +export const imagesArray: ImageData[] = JSON.parse(rawImageArray).sort( + (a: ImageData, b: ImageData) => { + if (a.index < b.index) { + return -1 + } + return 1 + } +) +export const imagesArrayLen: number = imagesArray.length + +// get layer divs +const layer5 = document.getElementById('layer5') as HTMLDivElement +const layer4 = document.getElementById('layer4') as HTMLDivElement +const layer3 = document.getElementById('layer3') as HTMLDivElement +const layer2 = document.getElementById('layer2') as HTMLDivElement +const layer1 = document.getElementById('layer1') as HTMLDivElement +export const layers: HTMLDivElement[] = [layer1, layer2, layer3, layer4, layer5] + +// layers position caching +export const posArray: string[][] = [ + ['0px', '0px', '0px', '0px', '0px'], + ['0px', '0px', '0px', '0px', '0px'] +] + +// global index for "activated" +let globalIndex: number = 0 + +// last position set as "activated" +let last: position = { x: 0, y: 0 } + +const activate = (index: number, x: number, y: number): void => { + const img = document.createElement('img') + img.setAttribute('src', imagesArray[index].url) + img.setAttribute('alt', imagesArray[index].index) + img.setAttribute('height', imagesArray[index].imgH) + img.setAttribute('width', imagesArray[index].imgW) + posCache(x, y, posArray) + layersPosSet(posArray, layers) + FIFO(img, layers) + // top + layer5.addEventListener( + 'click', + () => { + // stop images animation + window.removeEventListener('mousemove', handleOnMove) + // set top image + center(layer5) + layer5.dataset.status = 't0' + layer4.dataset.status = 't1' + layer3.dataset.status = 't2' + layer2.dataset.status = 't3' + layer1.dataset.status = 't4' + // overlay init + overlayEnable() + }, + { + passive: true, + once: true + } + ) + + last = { x, y } +} + +const distanceFromLast = (x: number, y: number): number => { + return Math.hypot(x - last.x, y - last.y) +} + +export const handleOnMove = (e: MouseEvent): void => { + // meet threshold + if ( + distanceFromLast(e.clientX, e.clientY) > + window.innerWidth / thresholdSensitivityArray[thresholdIndex] + ) { + // images showing array + const imageIndex = globalIndex % imagesArrayLen + // show top image and change index + activate(imageIndex, e.clientX, e.clientY) + imgIndexSpanUpdate(imageIndex + 1, imagesArrayLen) + // self increment + globalIndex++ + } +} + +export function trackMouseInit(): void { + window.addEventListener('mousemove', handleOnMove) +} diff --git a/assets/ts/utils.ts b/assets/ts/utils.ts new file mode 100644 index 0000000..f8aac43 --- /dev/null +++ b/assets/ts/utils.ts @@ -0,0 +1,78 @@ +export const posCache = (x: number, y: number, xyArray: string[][]): void => { + // pop element if length surpass limitation + xyArray[0].shift() + xyArray[1].shift() + // push new element + xyArray[0].push(`${x}px`) + xyArray[1].push(`${y}px`) +} + +export function duper(num: number): string { + return ('0000' + num.toString()).slice(-4) +} + +export const FIFO = ( + element: HTMLImageElement, + layersArray: HTMLDivElement[] +): void => { + function layerProcess(layerL: HTMLDivElement, layerH: HTMLDivElement): void { + if (layerL.childElementCount !== 0) + layerL.removeChild(layerL.lastElementChild as HTMLImageElement) + if (layerH.childElementCount !== 0) { + const layerHNode = layerH.lastElementChild as HTMLImageElement + layerL.appendChild(layerHNode.cloneNode(true)) + } + } + for (let i: number = 0; i <= 3; i++) { + layerProcess(layersArray[i], layersArray[i + 1]) + } + if (layersArray[4].childElementCount !== 0) + layersArray[4].removeChild(layersArray[4].lastElementChild as HTMLImageElement) + layersArray[4].appendChild(element) +} + +export const layersPosSet = ( + xyArray: string[][], + layersArray: HTMLDivElement[] +): void => { + function posSet(layer: HTMLDivElement, index: number): void { + layer.style.left = xyArray[0][index] + layer.style.top = xyArray[1][index] + } + for (let i = 4; i >= 0; i--) { + posSet(layersArray[i], i) + } +} + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export function delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +export function removeAllEventListeners(e: Node): Node { + return e.cloneNode(true) +} + +export const center = (e: HTMLDivElement): void => { + e.style.left = '50%' + if (window.innerWidth > 768) { + e.style.top = 'calc((100% - 38px) / 2)' + } else { + e.style.top = 'calc((100% - 31px) / 2 + 31px)' + } +} + +export function footerHeightUpdateInit(): void { + window.addEventListener( + 'resize', + () => { + const r = document.querySelector(':root') as HTMLElement + if (window.innerWidth > 768) { + r.style.setProperty('--footer-height', '38px') + } else { + r.style.setProperty('--footer-height', '31px') + } + }, + { passive: true } + ) +} diff --git a/layouts/index.html b/layouts/index.html index 7a16907..e41f9d3 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -24,7 +24,7 @@ {{ $.Scratch.Add "img" slice }} {{ range . }} {{ $index = sub $index 1}} - {{ $.Scratch.Add "img" (dict "index" (string $index) "url" (string .RelPermalink) "height" (string .Height) "width" (string .Width)) }} + {{ $.Scratch.Add "img" (dict "index" (string $index) "url" (string .RelPermalink) "imgH" (string .Height) "imgW" (string .Width)) }} {{ end }} {{ end }} diff --git a/layouts/partials/head.html b/layouts/partials/head.html index f71aec1..9c3d6da 100644 --- a/layouts/partials/head.html +++ b/layouts/partials/head.html @@ -4,5 +4,5 @@ {{- $style = dict "Context" . "ToCSS" $options | merge $style -}} {{- partial "plugin/style.html" $style -}} -{{ $js := resources.Get "js/theme.js" }} - \ No newline at end of file +{{ $script := resources.Get "ts/main.ts" | js.Build }} + \ No newline at end of file