transition from javascript to typescript

This commit is contained in:
Spedon
2023-03-14 01:01:23 +08:00
parent b05c475841
commit 211e2fd4fb
9 changed files with 343 additions and 236 deletions

View File

@@ -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'
})

17
assets/ts/indexDisp.ts Normal file
View File

@@ -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]
}
}
}

13
assets/ts/main.ts Normal file
View File

@@ -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()

85
assets/ts/overlay.ts Normal file
View File

@@ -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<void> {
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 }
)
}

40
assets/ts/thresholdCtl.ts Normal file
View File

@@ -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 }
)
}

107
assets/ts/trackMouse.ts Normal file
View File

@@ -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)
}

78
assets/ts/utils.ts Normal file
View File

@@ -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<void> {
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 }
)
}

View File

@@ -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 }}
<script id="images_array" type="application/json">{{ $.Scratch.Get "img" | jsonify | safeJS }}</script>
{{ end }}

View File

@@ -4,5 +4,5 @@
{{- $style = dict "Context" . "ToCSS" $options | merge $style -}}
{{- partial "plugin/style.html" $style -}}
{{ $js := resources.Get "js/theme.js" }}
<script type="text/javascript" src="{{ $js.RelPermalink }}" defer></script>
{{ $script := resources.Get "ts/main.ts" | js.Build }}
<script type="text/javascript" src="{{ $script.RelPermalink }}" defer></script>