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 {
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 {
calcImageIndex,
center,
createImgElement,
delay,
FIFO,
layerPosSet,
type position
} from './utils'
import { calcImageIndex, center, delay, mouseToTransform, type position } from './utils'
import { thresholdIndex, thresholdSensitivityArray } from './thresholdCtl'
import { imgIndexSpanUpdate } from './indexDisp'
import { imagesArray, imagesArrayLen } from './dataFetch'
import { layers } 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])
}
import { imagesArrayLen } from './dataFetch'
import { imagesDivNodes as images } from './elemGen'
// global index for "activated"
export let globalIndex: number = 0
@@ -27,13 +11,79 @@ export let globalIndex: number = 0
// last position set as "activated"
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
const activate = (index: number, x: number, y: number): void => {
const imgElem: HTMLImageElement = createImgElement(imagesArray[index])
topLayerPos = [x, y]
FIFO(imgElem, layers, true)
topLayerPosSet()
last = { x, y }
const activate = (index: number, mouseX: number, mouseY: number): void => {
EnterOverlayClickAbCtl.abort()
EnterOverlayClickAbCtl = new AbortController()
const indexesNum: number = pushIndex(index)
// set img position
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
@@ -61,10 +111,18 @@ export const handleOnMove = (e: MouseEvent): void => {
async function enterOverlay(): Promise<void> {
// stop images animation
window.removeEventListener('mousemove', handleOnMove)
// set top image
center(layers[0])
for (let i = 0; i <= 4; i++) {
layers[i].dataset.status = `t${i}`
const indexesNum: number = trailingImageIndexes.length
for (let i = 0; i < indexesNum; i++) {
const e: HTMLImageElement = images[trailingImageIndexes[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)
// Offset previous self increment of global index (by handleOnMove)
@@ -76,15 +134,6 @@ async function enterOverlay(): Promise<void> {
// initialization
export const trackMouseInit = (): void => {
window.addEventListener('mousemove', handleOnMove)
layers[0].addEventListener(
'click',
() => {
void enterOverlay()
},
{
passive: true
}
)
}
export const globalIndexDec = (): void => {
@@ -94,3 +143,11 @@ export const globalIndexDec = (): void => {
export const globalIndexInc = (): void => {
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
export let overlayCursor: HTMLDivElement
export let cursorInnerContent: HTMLDivElement
export let layers: HTMLDivElement[]
export let imagesDivNodes: NodeListOf<HTMLImageElement>
const passDesktopElements = (): void => {
overlayCursor = document
@@ -10,21 +13,8 @@ const passDesktopElements = (): void => {
cursorInnerContent = document
.getElementsByClassName('cursor_innerText')
.item(0) as HTMLDivElement
layers = [
document.getElementById('layer1') as HTMLDivElement,
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
imagesDivNodes = document.getElementsByClassName('images')[0]
.childNodes as NodeListOf<HTMLImageElement>
}
const createCursorDiv = (): HTMLDivElement => {
@@ -39,11 +29,11 @@ const createCursorDiv = (): HTMLDivElement => {
export const createDesktopElements = (): void => {
const mainDiv = document.getElementById('main') as HTMLDivElement
mainDiv.appendChild(createCursorDiv())
const desktopWrapper: HTMLDivElement = document.createElement('div')
desktopWrapper.className = 'desktopWrapper'
for (let i = 0; i < 15; i++) {
desktopWrapper.appendChild(createLayerDiv(i))
const imagesDiv: HTMLDivElement = document.createElement('div')
imagesDiv.className = 'images'
for (let i = 0; i < imagesArrayLen; i++) {
imagesDiv.appendChild(createImgElement(imagesArray[i]))
}
mainDiv.appendChild(desktopWrapper)
mainDiv.appendChild(imagesDiv)
passDesktopElements()
}

View File

@@ -1,21 +1,20 @@
import {
delay,
center,
createImgElement,
calcImageIndex,
FIFO,
mouseToTransform
} from './utils'
import { delay, center, calcImageIndex, mouseToTransform } from './utils'
import {
handleOnMove,
globalIndex,
globalIndexDec,
globalIndexInc,
topLayerPosSet
trailingImageIndexes,
transformCache,
pushIndex,
emptyTransformCache,
emptyTrailingImageIndexes
} from './desktop'
import { imagesArray, imagesArrayLen } from './dataFetch'
import { imagesArrayLen } from './dataFetch'
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
const setCursorText = (text: string): void => {
@@ -35,7 +34,7 @@ const disableListener = (): void => {
// enable overlay
export const overlayEnable = (): void => {
overlayCursor.style.zIndex = '7'
overlayCursor.style.zIndex = '100'
setListener()
}
@@ -44,41 +43,62 @@ export const overlayDisable = (): void => {
overlayCursor.style.zIndex = '-1'
setCursorText('')
disableListener()
// Add back previous self increment of global index (by handleOnMove)
globalIndexInc()
}
// handle close click
async function handleCloseClick(): Promise<void> {
overlayDisable()
topLayerPosSet()
for (let i: number = 0; i <= 4; i++) {
layers[i].dataset.status = `r${i}`
const indexesNum = trailingImageIndexes.length
emptyTrailingImageIndexes()
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)
for (let i: number = 0; i <= 4; i++) {
layers[i].dataset.status = 'null'
for (let i: number = 0; i < indexesNum; i++) {
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 })
emptyTransformCache()
}
const handlePrevClick = (): void => {
const imgIndex: number = calcImageIndex(globalIndex, imagesArrayLen)
globalIndexDec()
const imgIndex = calcImageIndex(globalIndex, imagesArrayLen)
FIFO(createImgElement(imagesArray[imgIndex]), layers, false)
imgIndexSpanUpdate(imgIndex + 1, imagesArrayLen)
const prevImgIndex = calcImageIndex(globalIndex, imagesArrayLen)
pushIndex(prevImgIndex, true, false)
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 imgIndex: number = calcImageIndex(globalIndex, imagesArrayLen)
globalIndexInc()
const imgIndex = calcImageIndex(globalIndex, imagesArrayLen)
FIFO(createImgElement(imagesArray[imgIndex]), layers, false)
imgIndexSpanUpdate(imgIndex + 1, imagesArrayLen)
const nextImgIndex = calcImageIndex(globalIndex, imagesArrayLen)
pushIndex(nextImgIndex, false, false)
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 => {
setTextPos(e)
const oneThird: number = Math.round(window.innerWidth / 3)
if (e.clientX < oneThird) {
setCursorText('PREV')
overlayCursor.dataset.status = 'PREV'
@@ -114,6 +134,7 @@ export const vwRefreshInit = (): void => {
window.addEventListener(
'resize',
() => {
oneThird = Math.round(window.innerWidth / 3)
// reset footer height
const r = document.querySelector(':root') as HTMLStyleElement
if (window.innerWidth > 768) {
@@ -122,7 +143,8 @@ export const vwRefreshInit = (): void => {
r.style.setProperty('--footer-height', '31px')
}
// 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 }
)

View File

@@ -23,29 +23,6 @@ export const duper = (num: number): string => {
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 = (
x: number,
y: number,
@@ -57,11 +34,6 @@ export const mouseToTransform = (
}, ${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
export function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms))
@@ -73,7 +45,7 @@ export const removeAllEventListeners = (e: Node): Node => {
}
// center top div
export const center = (e: HTMLDivElement): void => {
export const center = (e: HTMLElement): void => {
const x: number = window.innerWidth / 2
let y: number
if (window.innerWidth > 768) {
@@ -90,7 +62,9 @@ export const createImgElement = (input: ImageData): HTMLImageElement => {
img.setAttribute('alt', '')
img.setAttribute('height', input.imgH)
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
}