initial version of transition toward image elements

This commit is contained in:
Spedon
2023-03-24 16:11:00 +08:00
parent ef20aa5963
commit e897561afa
5 changed files with 207 additions and 116 deletions

View File

@@ -97,4 +97,52 @@
&#layer1 { &#layer1 {
z-index: 5; z-index: 5;
} }
}
.images {
img {
position: absolute;
top: 0;
left: 0;
object-fit: contain;
max-height: 55vmin;
max-width: 100vw;
&[data-status='null'] {
opacity: 1;
}
&[data-status='top'] {
opacity: 1;
max-height: calc(100vh - var(--footer-height));;
transition-property: transform, max-height;
transition-timing-function: ease-in-out;
transition-duration: 0.5s, 0.5s;
}
&[data-status='trail'] {
opacity: 0;
margin-top: 40px;
transition-property: opacity, margin-top;
transition-timing-function: ease-out;
transition-duration: 0.2s;
}
&[data-status='resumeTop'] {
opacity: 1;
max-height: 55vmin;
transition-property: max-height, transform;
transition-timing-function: ease-in-out;
transition-duration: 0.7s, 0.5s;
}
&[data-status='resume'] {
opacity: 1;
margin-top: 0;
transition-property: opacity, margin-top;
transition-timing-function: ease-out;
transition-duration: 0.2s;
}
}
} }

View File

@@ -1,25 +1,9 @@
import { overlayEnable } from './overlay' import { overlayEnable } from './overlay'
import { import { calcImageIndex, center, delay, mouseToTransform, type position } from './utils'
calcImageIndex,
center,
createImgElement,
delay,
FIFO,
layerPosSet,
type position
} from './utils'
import { thresholdIndex, thresholdSensitivityArray } from './thresholdCtl' import { thresholdIndex, thresholdSensitivityArray } from './thresholdCtl'
import { imgIndexSpanUpdate } from './indexDisp' import { imgIndexSpanUpdate } from './indexDisp'
import { imagesArray, imagesArrayLen } from './dataFetch' import { imagesArrayLen } from './dataFetch'
import { layers } from './elemGen' import { imagesDivNodes as images } from './elemGen'
// top layer position caching
let topLayerPos: number[] = [0, 0]
// set top layer position
export const topLayerPosSet = (): void => {
layerPosSet(topLayerPos[0], topLayerPos[1], layers[0])
}
// global index for "activated" // global index for "activated"
export let globalIndex: number = 0 export let globalIndex: number = 0
@@ -27,13 +11,79 @@ export let globalIndex: number = 0
// last position set as "activated" // last position set as "activated"
let last: position = { x: 0, y: 0 } let last: position = { x: 0, y: 0 }
export let trailingImageIndexes: number[] = []
export let transformCache: string[] = []
let EnterOverlayClickAbCtl = new AbortController()
export const stackDepth: number = 5
export const pushIndex = (
index: number,
invert: boolean = false,
autoHide: boolean = true
): number => {
let indexesNum: number = trailingImageIndexes.length
let overflow: number
if (!invert) {
// push the tail index out and hide the image
if (indexesNum === stackDepth) {
trailingImageIndexes.push(index)
overflow = trailingImageIndexes.shift() as number
if (autoHide) {
images[overflow].style.display = 'none'
images[overflow].dataset.status = 'trail'
}
} else {
trailingImageIndexes.push(index)
indexesNum += 1
}
} else {
if (indexesNum === stackDepth) {
trailingImageIndexes.unshift(
calcImageIndex(index - stackDepth + 1, imagesArrayLen)
)
overflow = trailingImageIndexes.pop() as number
if (autoHide) {
images[overflow].style.display = 'none'
images[overflow].dataset.status = 'trail'
}
} else {
trailingImageIndexes.unshift(
calcImageIndex(index - indexesNum + 1, imagesArrayLen)
)
indexesNum += 1
}
}
return indexesNum
}
// activate top image // activate top image
const activate = (index: number, x: number, y: number): void => { const activate = (index: number, mouseX: number, mouseY: number): void => {
const imgElem: HTMLImageElement = createImgElement(imagesArray[index]) EnterOverlayClickAbCtl.abort()
topLayerPos = [x, y] EnterOverlayClickAbCtl = new AbortController()
FIFO(imgElem, layers, true) const indexesNum: number = pushIndex(index)
topLayerPosSet() // set img position
last = { x, y } images[index].style.transform = mouseToTransform(mouseX, mouseY, true, true)
images[index].dataset.status = 'null'
// reset z index
for (let i = 0; i < indexesNum; i++) {
images[trailingImageIndexes[i]].style.zIndex = `${i}`
}
images[index].style.display = 'block'
images[index].addEventListener(
'click',
() => {
void enterOverlay()
},
{
passive: true,
once: true,
signal: EnterOverlayClickAbCtl.signal
}
)
last = { x: mouseX, y: mouseY }
} }
// Compare the current mouse position with the last activated position // Compare the current mouse position with the last activated position
@@ -61,10 +111,18 @@ export const handleOnMove = (e: MouseEvent): void => {
async function enterOverlay(): Promise<void> { async function enterOverlay(): Promise<void> {
// stop images animation // stop images animation
window.removeEventListener('mousemove', handleOnMove) window.removeEventListener('mousemove', handleOnMove)
// set top image const indexesNum: number = trailingImageIndexes.length
center(layers[0]) for (let i = 0; i < indexesNum; i++) {
for (let i = 0; i <= 4; i++) { const e: HTMLImageElement = images[trailingImageIndexes[i]]
layers[i].dataset.status = `t${i}` transformCache.push(e.style.transform)
if (i === indexesNum - 1) {
e.style.transitionDelay = `${0.1 * i + 0.2}s, ${0.1 * i + 0.2 + 0.5}s`
e.dataset.status = 'top'
center(e)
} else {
e.dataset.status = 'trail'
e.style.transitionDelay = `${0.1 * i}s`
}
} }
await delay(1600) await delay(1600)
// Offset previous self increment of global index (by handleOnMove) // Offset previous self increment of global index (by handleOnMove)
@@ -76,15 +134,6 @@ async function enterOverlay(): Promise<void> {
// initialization // initialization
export const trackMouseInit = (): void => { export const trackMouseInit = (): void => {
window.addEventListener('mousemove', handleOnMove) window.addEventListener('mousemove', handleOnMove)
layers[0].addEventListener(
'click',
() => {
void enterOverlay()
},
{
passive: true
}
)
} }
export const globalIndexDec = (): void => { export const globalIndexDec = (): void => {
@@ -94,3 +143,11 @@ export const globalIndexDec = (): void => {
export const globalIndexInc = (): void => { export const globalIndexInc = (): void => {
globalIndex++ globalIndex++
} }
export const emptyTransformCache = (): void => {
transformCache = []
}
export const emptyTrailingImageIndexes = (): void => {
trailingImageIndexes = []
}

View File

@@ -1,7 +1,10 @@
import { imagesArray, imagesArrayLen } from './dataFetch'
import { createImgElement } from './utils'
// get components of overlay // get components of overlay
export let overlayCursor: HTMLDivElement export let overlayCursor: HTMLDivElement
export let cursorInnerContent: HTMLDivElement export let cursorInnerContent: HTMLDivElement
export let layers: HTMLDivElement[] export let imagesDivNodes: NodeListOf<HTMLImageElement>
const passDesktopElements = (): void => { const passDesktopElements = (): void => {
overlayCursor = document overlayCursor = document
@@ -10,21 +13,8 @@ const passDesktopElements = (): void => {
cursorInnerContent = document cursorInnerContent = document
.getElementsByClassName('cursor_innerText') .getElementsByClassName('cursor_innerText')
.item(0) as HTMLDivElement .item(0) as HTMLDivElement
layers = [ imagesDivNodes = document.getElementsByClassName('images')[0]
document.getElementById('layer1') as HTMLDivElement, .childNodes as NodeListOf<HTMLImageElement>
document.getElementById('layer2') as HTMLDivElement,
document.getElementById('layer3') as HTMLDivElement,
document.getElementById('layer4') as HTMLDivElement,
document.getElementById('layer5') as HTMLDivElement
]
}
const createLayerDiv = (layerID: number): HTMLDivElement => {
const layerDiv: HTMLDivElement = document.createElement('div')
layerDiv.className = 'image_container'
layerDiv.id = `layer${layerID}`
layerDiv.dataset.status = 'null'
return layerDiv
} }
const createCursorDiv = (): HTMLDivElement => { const createCursorDiv = (): HTMLDivElement => {
@@ -39,11 +29,11 @@ const createCursorDiv = (): HTMLDivElement => {
export const createDesktopElements = (): void => { export const createDesktopElements = (): void => {
const mainDiv = document.getElementById('main') as HTMLDivElement const mainDiv = document.getElementById('main') as HTMLDivElement
mainDiv.appendChild(createCursorDiv()) mainDiv.appendChild(createCursorDiv())
const desktopWrapper: HTMLDivElement = document.createElement('div') const imagesDiv: HTMLDivElement = document.createElement('div')
desktopWrapper.className = 'desktopWrapper' imagesDiv.className = 'images'
for (let i = 0; i < 15; i++) { for (let i = 0; i < imagesArrayLen; i++) {
desktopWrapper.appendChild(createLayerDiv(i)) imagesDiv.appendChild(createImgElement(imagesArray[i]))
} }
mainDiv.appendChild(desktopWrapper) mainDiv.appendChild(imagesDiv)
passDesktopElements() passDesktopElements()
} }

View File

@@ -1,21 +1,20 @@
import { import { delay, center, calcImageIndex, mouseToTransform } from './utils'
delay,
center,
createImgElement,
calcImageIndex,
FIFO,
mouseToTransform
} from './utils'
import { import {
handleOnMove, handleOnMove,
globalIndex, globalIndex,
globalIndexDec, globalIndexDec,
globalIndexInc, globalIndexInc,
topLayerPosSet trailingImageIndexes,
transformCache,
pushIndex,
emptyTransformCache,
emptyTrailingImageIndexes
} from './desktop' } from './desktop'
import { imagesArray, imagesArrayLen } from './dataFetch' import { imagesArrayLen } from './dataFetch'
import { imgIndexSpanUpdate } from './indexDisp' import { imgIndexSpanUpdate } from './indexDisp'
import { overlayCursor, cursorInnerContent, layers } from './elemGen' import { overlayCursor, cursorInnerContent, imagesDivNodes as images } from './elemGen'
let oneThird: number = Math.round(window.innerWidth / 3)
// set cursor text // set cursor text
const setCursorText = (text: string): void => { const setCursorText = (text: string): void => {
@@ -35,7 +34,7 @@ const disableListener = (): void => {
// enable overlay // enable overlay
export const overlayEnable = (): void => { export const overlayEnable = (): void => {
overlayCursor.style.zIndex = '7' overlayCursor.style.zIndex = '100'
setListener() setListener()
} }
@@ -44,41 +43,62 @@ export const overlayDisable = (): void => {
overlayCursor.style.zIndex = '-1' overlayCursor.style.zIndex = '-1'
setCursorText('') setCursorText('')
disableListener() disableListener()
// Add back previous self increment of global index (by handleOnMove)
globalIndexInc()
} }
// handle close click // handle close click
async function handleCloseClick(): Promise<void> { async function handleCloseClick(): Promise<void> {
overlayDisable() overlayDisable()
topLayerPosSet() const indexesNum = trailingImageIndexes.length
for (let i: number = 0; i <= 4; i++) { emptyTrailingImageIndexes()
layers[i].dataset.status = `r${i}` for (let i: number = 0; i < indexesNum; i++) {
const e: HTMLImageElement = images[calcImageIndex(globalIndex - i, imagesArrayLen)]
trailingImageIndexes.unshift(calcImageIndex(globalIndex - i, imagesArrayLen))
if (i === 0) {
e.style.transitionDelay = '0s, 0.7s'
} else {
e.style.transitionDelay = `${1.2 + 0.1 * i - 0.1}s`
e.style.display = 'block'
}
e.style.transform = transformCache[indexesNum - i - 1]
e.style.zIndex = `${indexesNum - i - 1}`
e.dataset.status = i === 0 ? 'resumeTop' : 'resume'
} }
await delay(1700) await delay(1700)
for (let i: number = 0; i <= 4; i++) { for (let i: number = 0; i < indexesNum; i++) {
layers[i].dataset.status = 'null' images[calcImageIndex(globalIndex - i, imagesArrayLen)].dataset.status = 'null'
} }
// Add back previous self increment of global index (by handleOnMove)
globalIndexInc()
window.addEventListener('mousemove', handleOnMove, { passive: true }) window.addEventListener('mousemove', handleOnMove, { passive: true })
emptyTransformCache()
} }
const handlePrevClick = (): void => { const handlePrevClick = (): void => {
const imgIndex: number = calcImageIndex(globalIndex, imagesArrayLen)
globalIndexDec() globalIndexDec()
const imgIndex = calcImageIndex(globalIndex, imagesArrayLen) const prevImgIndex = calcImageIndex(globalIndex, imagesArrayLen)
FIFO(createImgElement(imagesArray[imgIndex]), layers, false) pushIndex(prevImgIndex, true, false)
imgIndexSpanUpdate(imgIndex + 1, imagesArrayLen) images[imgIndex].style.display = 'none'
center(images[prevImgIndex])
images[prevImgIndex].dataset.status = 'top'
images[prevImgIndex].style.display = 'block'
imgIndexSpanUpdate(prevImgIndex + 1, imagesArrayLen)
} }
const handleNextClick = (): void => { const handleNextClick = (): void => {
const imgIndex: number = calcImageIndex(globalIndex, imagesArrayLen)
globalIndexInc() globalIndexInc()
const imgIndex = calcImageIndex(globalIndex, imagesArrayLen) const nextImgIndex = calcImageIndex(globalIndex, imagesArrayLen)
FIFO(createImgElement(imagesArray[imgIndex]), layers, false) pushIndex(nextImgIndex, false, false)
imgIndexSpanUpdate(imgIndex + 1, imagesArrayLen) images[imgIndex].style.display = 'none'
center(images[nextImgIndex])
images[nextImgIndex].dataset.status = 'top'
images[nextImgIndex].style.display = 'block'
imgIndexSpanUpdate(nextImgIndex + 1, imagesArrayLen)
} }
const handleOverlayMouseMove = (e: MouseEvent): void => { const handleOverlayMouseMove = (e: MouseEvent): void => {
setTextPos(e) setTextPos(e)
const oneThird: number = Math.round(window.innerWidth / 3)
if (e.clientX < oneThird) { if (e.clientX < oneThird) {
setCursorText('PREV') setCursorText('PREV')
overlayCursor.dataset.status = 'PREV' overlayCursor.dataset.status = 'PREV'
@@ -114,6 +134,7 @@ export const vwRefreshInit = (): void => {
window.addEventListener( window.addEventListener(
'resize', 'resize',
() => { () => {
oneThird = Math.round(window.innerWidth / 3)
// reset footer height // reset footer height
const r = document.querySelector(':root') as HTMLStyleElement const r = document.querySelector(':root') as HTMLStyleElement
if (window.innerWidth > 768) { if (window.innerWidth > 768) {
@@ -122,7 +143,8 @@ export const vwRefreshInit = (): void => {
r.style.setProperty('--footer-height', '31px') r.style.setProperty('--footer-height', '31px')
} }
// recenter image (only in overlay) // recenter image (only in overlay)
if (layers[0].dataset.status === 't0') center(layers[0]) const i: HTMLImageElement = images[calcImageIndex(globalIndex, imagesArrayLen)]
if (i.dataset.status === 'top') center(i)
}, },
{ passive: true } { passive: true }
) )

View File

@@ -23,29 +23,6 @@ export const duper = (num: number): string => {
return ('0000' + num.toString()).slice(-4) return ('0000' + num.toString()).slice(-4)
} }
// FIFO data array for image display
export const FIFO = (
element: HTMLImageElement,
layersArray: HTMLDivElement[],
passPosition: boolean = true
): 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))
if (passPosition) layerL.style.transform = layerH.style.transform
}
}
for (let i: number = 4; i >= 1; i--) {
layerProcess(layersArray[i], layersArray[i - 1])
}
if (layersArray[0].childElementCount !== 0)
layersArray[0].removeChild(layersArray[0].lastElementChild as HTMLImageElement)
layersArray[0].appendChild(element)
}
export const mouseToTransform = ( export const mouseToTransform = (
x: number, x: number,
y: number, y: number,
@@ -57,11 +34,6 @@ export const mouseToTransform = (
}, ${centerCorrection ? `calc(${y}px - 50%)` : `${y}px`}${accelerate ? ', 0' : ''})` }, ${centerCorrection ? `calc(${y}px - 50%)` : `${y}px`}${accelerate ? ', 0' : ''})`
} }
// set position for layer
export const layerPosSet = (x: number, y: number, layer: HTMLDivElement): void => {
layer.style.transform = mouseToTransform(x, y)
}
// eslint-disable-next-line @typescript-eslint/promise-function-async // eslint-disable-next-line @typescript-eslint/promise-function-async
export function delay(ms: number): Promise<void> { export function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)) return new Promise((resolve) => setTimeout(resolve, ms))
@@ -73,7 +45,7 @@ export const removeAllEventListeners = (e: Node): Node => {
} }
// center top div // center top div
export const center = (e: HTMLDivElement): void => { export const center = (e: HTMLElement): void => {
const x: number = window.innerWidth / 2 const x: number = window.innerWidth / 2
let y: number let y: number
if (window.innerWidth > 768) { if (window.innerWidth > 768) {
@@ -90,7 +62,9 @@ export const createImgElement = (input: ImageData): HTMLImageElement => {
img.setAttribute('alt', '') img.setAttribute('alt', '')
img.setAttribute('height', input.imgH) img.setAttribute('height', input.imgH)
img.setAttribute('width', input.imgW) img.setAttribute('width', input.imgW)
img.style.backgroundImage = `linear-gradient(15deg, ${input.pColor}, ${input.sColor})` img.style.display = 'none'
img.dataset.status = 'trail'
// img.style.backgroundImage = `linear-gradient(15deg, ${input.pColor}, ${input.sColor})`
return img return img
} }