使用方法
const {
EventBus,
Events,
Plugin,
createApp,
DragEventPlugin,
DragEvent
} = Veloxi
const OFFSET = 10
class SetActiveIndexEvent {
index
constructor({ index }) {
this.index = index
}
}
class NavItem {
view
index
container
initialized = false
constructor(view, index, container) {
this.view = view
this.index = index
this.container = container
this.view.position.animator.set('dynamic', { speed: 5 })
this.view.scale.animator.set('dynamic', { speed: 5 })
}
init() {
requestAnimationFrame(() => {
this.enableTransition()
})
this.initialized = true
}
enableTransition() {
this.view.styles.transition = '0.2s opacity linear'
}
update() {
this.updatePosition()
this.updateOpacity()
this.updateScale()
}
updateWithOffset(offset) {
const targetSlot = offset > 0 ? this.nextSlot : this.previousSlot
const percentage = Math.abs(offset / this.container.stepSize)
this.updatePositionWithOffset(offset, targetSlot, percentage)
this.updateOpacityWithPercentage(targetSlot, percentage)
this.updateScaleWithPercentage(targetSlot, percentage)
}
updatePositionWithOffset(offset, targetSlot, percentage) {
const x = this.currentPosition.x
let y = this.currentPosition.y
switch (targetSlot) {
case slotIndex.ACTIVE:
y -= 10 * percentage
break
case slotIndex.FIRST_NEXT:
case slotIndex.FIRST_PREVIOUS:
const fromActive = this.slotIndex === slotIndex.ACTIVE
y += 25 * (fromActive ? 1 : -1) * percentage
break
case slotIndex.SECOND_NEXT:
case slotIndex.SECOND_PREVIOUS:
const fromFirst = [
slotIndex.FIRST_NEXT,
slotIndex.FIRST_PREVIOUS
].includes(this.slotIndex)
y += 40 * percentage * (fromFirst ? 1 : -1)
break
default:
const fromSecond = [
slotIndex.SECOND_NEXT,
slotIndex.SECOND_PREVIOUS
].includes(this.slotIndex)
y += 40 * percentage * (fromSecond ? 1 : -1)
}
this.view.position.set({ x: x + offset, y })
}
updateScaleWithPercentage(targetSlot, percentage) {
let scale = this.currentScale
switch (targetSlot) {
case slotIndex.ACTIVE:
scale += 0.1 * percentage
break
case slotIndex.FIRST_NEXT:
case slotIndex.FIRST_PREVIOUS:
const fromActive = this.slotIndex === slotIndex.ACTIVE
if (fromActive) {
scale -= 0.1 * percentage
}
break
}
this.view.scale.set({ x: scale, y: scale })
}
updateOpacityWithPercentage(targetSlot, percentage) {
let opacity = this.currentOpacity
switch (targetSlot) {
case slotIndex.ACTIVE:
opacity += 0.2 * percentage
break
case slotIndex.FIRST_PREVIOUS:
case slotIndex.FIRST_NEXT:
const fromActive = this.slotIndex === slotIndex.ACTIVE
opacity += 0.2 * percentage * (fromActive ? -1 : 1)
break
case slotIndex.SECOND_PREVIOUS:
case slotIndex.SECOND_NEXT:
const fromFirst = [
slotIndex.FIRST_NEXT,
slotIndex.FIRST_PREVIOUS
].includes(this.slotIndex)
if (!fromFirst) {
if (
this.firstItemIndexInStart === this.index ||
this.firstItemIndexInEnd === this.index
) {
opacity += 0.2 * percentage * (fromFirst ? -1 : 1)
} else {
opacity = 0
}
} else {
opacity += 0.2 * percentage * (fromFirst ? -1 : 1)
}
break
default:
const fromSecond = [
slotIndex.SECOND_NEXT,
slotIndex.SECOND_PREVIOUS
].includes(this.slotIndex)
if (fromSecond) {
opacity += 0.4 * percentage * (fromSecond ? -1 : 1)
} else {
opacity = 0
}
}
this.view.styles.opacity = `${opacity}`
}
updatePosition() {
const shouldAnimate = this.container.shouldAnimateMap.get(this.index)
const x = this.slotPosition.x
let y = this.slotPosition.y
switch (this.slotIndex) {
case slotIndex.ACTIVE:
break
case slotIndex.FIRST_NEXT:
case slotIndex.FIRST_PREVIOUS:
y += 10
break
case slotIndex.SECOND_NEXT:
case slotIndex.SECOND_PREVIOUS:
y += 40
break
default:
y += 100
}
this.view.position.set({ x, y }, shouldAnimate)
}
get currentPosition() {
const x = this.slotPosition.x
let y = this.slotPosition.y
switch (this.slotIndex) {
case slotIndex.ACTIVE:
break
case slotIndex.FIRST_NEXT:
case slotIndex.FIRST_PREVIOUS:
y += 10
break
case slotIndex.SECOND_NEXT:
case slotIndex.SECOND_PREVIOUS:
y += 40
break
default:
y += 80
}
return { x, y }
}
get currentOpacity() {
let opacity = 0
switch (this.slotIndex) {
case slotIndex.ACTIVE:
opacity = 1
break
case slotIndex.FIRST_PREVIOUS:
case slotIndex.FIRST_NEXT:
opacity = 0.425
break
case slotIndex.SECOND_PREVIOUS:
case slotIndex.SECOND_NEXT:
opacity = 0.2
break
}
return opacity
}
get currentScale() {
let scale = 0.75
switch (this.slotIndex) {
case slotIndex.ACTIVE:
scale = 1
break
case slotIndex.FIRST_PREVIOUS:
case slotIndex.FIRST_NEXT:
scale = 0.75
break
case slotIndex.SECOND_PREVIOUS:
case slotIndex.SECOND_NEXT:
scale = 0.75
break
}
return scale
}
updateOpacity() {
let opacity = 0
switch (this.slotIndex) {
case slotIndex.ACTIVE:
opacity = 1
break
case slotIndex.FIRST_PREVIOUS:
case slotIndex.FIRST_NEXT:
opacity = 0.425
break
case slotIndex.SECOND_PREVIOUS:
case slotIndex.SECOND_NEXT:
opacity = 0.2
break
}
this.view.styles.opacity = `${opacity}`
}
updateScale() {
let scale = 0.75
switch (this.slotIndex) {
case slotIndex.ACTIVE:
scale = 1
break
case slotIndex.FIRST_PREVIOUS:
case slotIndex.FIRST_NEXT:
scale = 0.75
break
case slotIndex.SECOND_PREVIOUS:
case slotIndex.SECOND_NEXT:
scale = 0.75
break
}
this.view.scale.set({ x: scale, y: scale }, this.initialized)
}
get nextSlot() {
return wrapAround(this.slotIndex, Object.keys(slotIndex).length, 1)
}
get previousSlot() {
return wrapAround(this.slotIndex, Object.keys(slotIndex).length, -1)
}
get slotPosition() {
return this.container.getSlotPositionForItemIndex(this.index)
}
get activeIndex() {
return this.container.activeIndex
}
get slotIndex() {
return this.container.getSlotForIndex(this.index)
}
getItemIndexForSlot(slot) {
return this.container.getItemIndeciesForSlot(slot)[0]
}
get firstItemIndexInStart() {
const secondPreviousIndex = this.getItemIndexForSlot(
slotIndex.SECOND_PREVIOUS
)
return wrapAround(secondPreviousIndex, this.container.totalItems, -1)
}
get firstItemIndexInEnd() {
const secondNextItemIndex = this.getItemIndexForSlot(
slotIndex.SECOND_NEXT
)
return wrapAround(secondNextItemIndex, this.container.totalItems, 1)
}
}
const slotIndex = {
START: 0,
SECOND_PREVIOUS: 1,
FIRST_PREVIOUS: 2,
ACTIVE: 3,
FIRST_NEXT: 4,
SECOND_NEXT: 5,
END: 6
}
function wrapAround(current, total, amount) {
return (current + total + amount) % total
}
function flipMap(map) {
const flippedMap = new Map()
for (const [key, value] of map) {
if (!flippedMap.has(value)) {
flippedMap.set(value, [key])
} else {
flippedMap.get(value).push(key)
}
}
return flippedMap
}
class NavContainer {
plugin
view
activeIndex
shouldAnimateMap = new Map()
_itemIndexSlotMap = new Map()
_slotItemIndexMap = new Map()
constructor(plugin, view) {
this.plugin = plugin
this.view = view
this.activeIndex = this.view.data.activeIndex
? parseInt(this.view.data.activeIndex)
: 0
for (let index = 0; index < this.totalItems; index++) {
this.shouldAnimateMap.set(index, false)
}
}
get stepSize() {
return this.plugin.stepSize
}
get itemSize() {
return this.plugin.itemSize
}
get totalItems() {
return this.plugin.totalItems
}
updateWithOffset(offset) {
const steps = Math.floor(Math.abs(offset / this.stepSize))
const queue = []
let currentIndex = this.activeIndex
for (let step = 0; step < steps; step++) {
const stepDirection = offset < 0 ? 1 : -1
const itemIndex = wrapAround(
currentIndex,
this.totalItems,
stepDirection
)
queue.push(itemIndex)
currentIndex = itemIndex
}
queue.forEach((itemIndex, index) => {
setTimeout(() => {
this.plugin.setActiveIndex(itemIndex)
}, 100 * index)
})
}
setActiveIndex(newActiveIndex) {
const previousItemIndexSlot = this.itemIndexSlotMap
this.activeIndex = newActiveIndex
this.setItemIndexSlotMap()
const newItemIndexSlot = this.itemIndexSlotMap
const visibleSlots = [
slotIndex.ACTIVE,
slotIndex.FIRST_PREVIOUS,
slotIndex.SECOND_PREVIOUS,
slotIndex.FIRST_NEXT,
slotIndex.SECOND_NEXT
]
for (let index = 0; index < this.totalItems; index++) {
const shouldAnimate =
visibleSlots.includes(previousItemIndexSlot.get(index)) ||
visibleSlots.includes(newItemIndexSlot.get(index))
this.shouldAnimateMap.set(index, shouldAnimate)
}
}
get slotItemIndexMap() {
return this._slotItemIndexMap
}
get itemIndexSlotMap() {
return this._itemIndexSlotMap
}
getSlotForIndex(itemIndex) {
return this.itemIndexSlot
站长提示:
1. 平台上所有素材资源,需注册登录会员方能正常下载。
2. 会员用户积极反馈网站、素材资源BUG或错误问题,每次奖励
20K币。
3. PHP源码类素材,如需协助安装调试,或你有二次开发需求,可联系苦力吧客服。
4. 付费素材资源,需充值后方能下载,如有任何疑问可直接联系苦力吧客服