need:
- The button is displayed floating on the side of the page;
- Click the button to expand multiple shortcut buttons from bottom to top.
- Long press the button to allow dragging to change the button position, and the button is in a non-expanded state;
- When the button movement is completed and your finger is released, the distance to the left and right sides is calculated and automatically moved to the side display;
- After moving to the side, the expansion style is changed based on the specific left and right positions;
- Handle special cases when moving to non-visible areas.
Show results:
Implementation:
<template> <div class="shortcut" @touchstart="touchstart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)" v-if="isMobile"> <div class="shortcut__container"> <transition name="fade"> <div class="shadow" v-if="showPopover" @click.stop="showPopover = false"></div> </transition> <transition name="sub-fade"> <div :class="['shortcut__list', `${type}`]" v-if="showPopover"> <div class="shortcut__list_item"> <div class="icon-box"><img src="@/images/common/ic_question.png" alt=""></div> Investment and financial management </div> <div class="shortcut__list_item"> <div class="icon-box"><img src="@/images/common/ic_question.png" alt=""></div> my assets </div> <div class="shortcut__list_item"> <div class="icon-box"><img src="@/images/common/ic_question.png" alt=""></div> Consult us </div> </div> </transition> <div class="shortcut__btn" :class="{ anim: showPopover }" @click.stop="handleBtn()"> + </div> </div> </div> </template> <script> const TIME = 50 export default {<!-- --> data () {<!-- --> return {<!-- --> isMobile: /Mobi|Android|iPhone/i.test(navigator.userAgent), showPopover: false, timeOutEvent: 0, longClick: 0, //Finger original position oldMousePos: {<!-- -->}, //original position of element oldNodePos: {<!-- -->}, type: 'right', } }, methods: {<!-- --> touchstart (ev) {<!-- --> // The timer controls the long press time, and dragging starts after {TIME} milliseconds. this.timeOutEvent = setTimeout(() => {<!-- --> this.longClick = 1 }, TIME) const selectDom = ev.currentTarget const {<!-- --> pageX, pageY } = ev.touches[0] // Finger position const {<!-- --> offsetLeft, offsetTop } = selectDom // element position //Finger original position this.oldMousePos = {<!-- --> x: pageX, y: pageY, } //original position of element this.oldNodePos = {<!-- --> x: offsetLeft, y: offsetTop, } this.handleMoving() selectDom.style.left = `${<!-- -->offsetLeft}px` selectDom.style.top = `${<!-- -->offsetTop}px` }, touchMove (ev) {<!-- --> // If you move before {TIME} milliseconds, the long press will not be triggered and the timer will be cleared. clearTimeout(this.timeOutEvent) if (this.longClick === 1) {<!-- --> this.handleMoving() this.showPopover = false const selectDom = ev.currentTarget // x-axis offset const lefts = this.oldMousePos.x - this.oldNodePos.x // y-axis offset const tops = this.oldMousePos.y - this.oldNodePos.y const {<!-- --> pageX, pageY } = ev.touches[0] // Finger position selectDom.style.left = `${<!-- -->pageX - lefts}px` selectDom.style.top = `${<!-- -->pageY - tops}px` } }, touchEnd (ev) {<!-- --> //Clear the timer clearTimeout(this.timeOutEvent) if (this.longClick === 1) {<!-- --> this.longClick = 0 const selectDom = ev.currentTarget const {<!-- --> innerWidth, innerHeight } = window const {<!-- --> offsetLeft, offsetTop } = selectDom selectDom.style.left = offsetLeft + 50 > innerWidth / 2 ? 'calc(100% - 55px)' : '15px' if (offsetTop < 150) {<!-- --> selectDom.style.top = '150px' } else if (offsetTop + 150 > innerHeight) {<!-- --> selectDom.style.top = `${<!-- -->innerHeight - 150}px` } this.type = offsetLeft + 50 > innerWidth / 2 ? 'right' : 'left' setTimeout(() => {<!-- --> document.body.style.overflow = 'auto' document.body.style.userSelect = 'auto' }, 1000) } }, handleMoving () {<!-- --> // Disable body scrolling document.body.style.overflow = 'hidden' // Disable body text selection document.body.style.userSelect = 'none' }, handleBtn () {<!-- --> this.showPopover = !this.showPopover } }, } </script> <style scoped lang="less"> .icon-box {<!-- --> background: #fff; width: .8rem; height: .8rem; border-radius: 50%; display: flex; align-items: center; justify-content: center; } .shortcut {<!-- --> position: fixed; z-index: 9999; left: calc(100% - 55px); top: calc(100% - 150px); user-select: none; & amp;__container {<!-- --> position: relative; } & amp;__list {<!-- --> position: absolute; bottom: .8rem; z-index: 8; & amp;_item {<!-- --> color: #fff; display: flex; flex-direction: row; align-items: center; white-space: nowrap; margin-bottom: .15rem; .icon-box {<!-- --> margin: 0 .1rem 0 0; img {<!-- --> width: 0.36rem; height: 0.36rem; } } } & amp;.left {<!-- --> left: 0; } & amp;.right {<!-- --> right: 0; .shortcut__list_item {<!-- --> flex-direction: row-reverse; .icon-box {<!-- --> margin: 0 0 0 .1rem; } } } } & amp;__btn {<!-- --> background: #fff; width: .8rem; height: .8rem; border-radius: 50%; text-align: center; line-height: .7rem; color: #3356D9; font-size: .5rem; position: relative; z-index: 8; border: 1px solid #3356D9; transition: all .3s linear; & amp;.anim {<!-- --> transform: rotate(135deg); } } } .shadow {<!-- --> width: 100%; max-width: 1024px; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 1; margin: 0 auto; } .sub-fade-leave-active,.sub-fade-enter-active {<!-- --> transition: max-height 0.3s linear; } .sub-fade-enter,.sub-fade-leave-to {<!-- --> max-height: 0; overflow: hidden; } .sub-fade-enter-to,.sub-fade-leave {<!-- --> max-height: 2.56rem; overflow: hidden; } </style>