纯js创建的环形点击切换导航菜单滑块特效代码

所属分类: 网页特效-导航菜单    2024-02-03 10:38:58

纯js创建的环形点击切换导航菜单滑块特效代码 ie兼容6
反馈问题  查看演示  登录后下载 温馨提示
登录会员即可享受免费下载
 我要建站

纯js创建的环形点击切换导航菜单滑块特效代码(共3个文件)

    • index.html

使用方法

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. 付费素材资源,需充值后方能下载,如有任何疑问可直接联系苦力吧客服
相关资源 / 导航菜单

jquery文件夹树形结构插件

一款多功能tree左侧导航下拉菜单插件,接口丰富,简单实用!
  导航菜单
 185  

js移动端导航菜单固定底部代码

一款手机移动端导航菜单固定底部,带图标和菜单名称固定于页面底部,常用于手机端网站。
  导航菜单
 105  

原生js可展开收缩的侧边栏下拉菜单

一款固定于左侧边栏的导航菜单,带二级下拉菜单手风琴特效,很实用!
  导航菜单
 177  

jquery强大的自定义黑色导航菜单带5种动态效果

带5种不同动态效果的导航条,很炫酷很丝滑的特效。
  导航菜单
 127  

评论数(0) 回复有机会获得K币 用户协议

^_^ 还没有人评论,快来抢个沙发!
😀
  • 😀
  • 😊
  • 😂
  • 😍
  • 😑
  • 😷
  • 😵
  • 😛
  • 😣
  • 😱
  • 😋
  • 😎
  • 😵
  • 😕
  • 😶
  • 😚
  • 😜
  • 😭
发表评论