微信小程序浮动按钮_微信小程序浮动按钮的实现

需求

产品要求在每个页面上都加一个浮动按钮,上面一个电话图标,点击后给VIP联系人拨打电话。我想这个简单,一两个小时应该就实现了吧。于是我折腾了差不多两天。

初步构想

记得以前看小程序组件文档时有一个movable-view,可以拿来用一下。结果真正使用时才发现,movable-view外面需要套一个movable-area才可以。没关系,套上好了。

首先,建一个组件,叫phone好了,最外层是movable-area,里面是movable-view,再里面是一个普通view,加一个hover-class可以实现点击状态。把这个组件加个每一个页面上。

movable-view的移动范围是根据外层的movable-area来确定的,所以movable-area应该铺满全屏,但这样一来就把页面上其他内容全部挡住了。那就利用touchstart/touchend事件判断是浮动按钮是否被按下,按下的状态就设置movable-area大小为按钮大小,位置为按钮的位置(通过设置transform样式实现),而没有按下的状态,再让它恢复成全屏大小,位置为(0,0)(transform:none).

试验了一下,初始位置调为左上角,第一次拖动是正常的,第二次拖就不对劲了。虽然第二次拖动movable-view的初始位置是正确的,但它的change事件中得到的坐标却是从(0,0)也就是被点击那一瞬间开始算的,虽说此时已经把movable-area的位置、大小恢复了。

通用方案

既然movable-view不好用,那就改成普通view算了,通过touchmove事件改变其位置,这样应该比较简单,虽说可能不如movable-view拖起来流畅。但对于支持下拉刷新的页面,拖动浮动按钮时,往往就下拉刷新了。此时即使catch页面上某些veiw的touchmove方法,也是不管用的。因为下拉刷新不是view的下拉,而是整个页面的下拉,也没有办法阻止冒泡什么的。自定义Modal的mask层也有同样的问题,但好在一般用户不会去拖动mask层,但浮动按钮却是必拖无疑问的。

如果可以忍的话,这个方案是可行的。但是,不能忍啊。于是我开始了无休止的纠结,甚至一度想干脆让这个浮动按钮直接固定在右下角算了。

最终实现

又经过长时间的反复试验,我发现movable-view直接用也是可以的,但直接用的话change事件不太灵敏,干脆不让它change,能不让页面下拉刷新也是好的。不过呢,direction设为none以后,它的表现和普通view一样,拖动时页面照样会下拉刷新。最后只得设成了vertical,这样会有一个小小的bug,让最终的方案不是那么完美。

下面是我的代码:

components/phone/index.wxml

components/phone/index.wxss

.make-phone-call-movable {

display: block;

position: fixed;

width: 108rpx;

height: 108rpx;

z-index: 1301;

}

.make-phone-call-movable.hidden {

display: none;

}

.make-phone-call {

background-color: #C00000;

background-image: url('data:image/png;base64,...');

background-repeat: no-repeat;

background-position: 28rpx 28rpx;

background-size: 52rpx 52rpx;

border-radius: 50%;

display: block;

position: absolute;

left: 0;

top: 0;

width: 108rpx;

height: 108rpx;

z-index: 1301;

}

.make-phone-call-hover {

background-color: #B40000;

}

components/phone/index.js

const app = getApp()

const util = require('../../utils/util.js')

const POSITION_KEY = 'phoneCallButtonPosition'

Component({

properties: {

hasTabbar: {

type: Boolean,

value: false

}

},

data: {

name: '',

mobile: '',

rectangless: {

left: 8,

top: 8,

right: 313,

bottom: 486

},

touched: false,

left: 0,

top: 0

},

methods: {

onTouchMove(e) {

const currentX = e.touches[0].pageX

const currentY = e.touches[0].pageY

let left = this.lastLeft + currentX - this.lastX

let top = this.lastTop + currentY - this.lastY

left = Math.min(Math.max(left, this.data.rectangle.left), this.data.rectangle.right)

top = Math.min(Math.max(top, this.data.rectangle.top), this.data.rectangle.bottom)

this.setData({

left: left,

top: top

})

},

onTouchStart(e) {

this.lastX = e.touches[0].pageX

this.lastY = e.touches[0].pageY

this.lastLeft = this.data.left

this.lastTop = this.data.top

this.setData({

touched: true

})

},

onTouchEnd(e) {

this.setData({

touched: false

})

const leftDiff = Math.abs(this.lastLeft - this.data.left)

const topDiff = Math.abs(this.lastTop - this.data.top)

if (leftDiff === 0 && topDiff === 0) {

this.makePhoneCall()

} else {

this.playAnimation()

}

},

makePhoneCall() {

const name = this.data.name

const mobile = this.data.mobile

const language = app.globalData.language

if (!mobile) return

let message = util.getStrings('app', language).phone_call

message = message.replace(/{contact}/g, name)

util.showConfirmAlert(language, message, () => {

wx.makePhoneCall({

phoneNumber: mobile

})

})

},

getMobile() {

app.doGet('getMemberInfo', {}).then(res => {

let name = ''

let mobile = ''

if (res && res.contact) {

name = res.contact.name ? res.contact.name : '',

mobile = res.contact.mobile ? res.contact.mobile : ''

}

this.setData({

name: name,

mobile: mobile

})

}).catch(e => {

console.error(e)

this.setData({

name: '',

mobile: ''

})

})

},

getRectangle() {

const systemInfo = util.getSystemInfoSync()

const windowWidth = systemInfo.windowWidth

const windowHeight = systemInfo.windowHeight

const buttonSize = Math.round(windowWidth * 108 / 750)

const tabbarHeight = this.properties.hasTabbar ?

Math.round(windowWidth * 110 / 750) : 0

const margin = 8

const right = windowWidth - margin - buttonSize

const bottom = windowHeight - margin - tabbarHeight - buttonSize

this.setData({

rectangle: {

left: margin,

top: margin,

right: right,

bottom: bottom,

windowWidth: windowWidth,

windowHeight: windowHeight,

buttonSize: buttonSize,

tabbarHeight: tabbarHeight,

margin: margin

}

})

},

getPosition() {

const position = util.getStorageSync(POSITION_KEY)

this.getRectangle()

let left = this.data.rectangle.right

let top = this.data.rectangle.bottom

if (position) {

left = position.left

top = position.top

}

this.setData({

left: left,

top: top

})

},

playAnimation() {

const rectangle = this.data.rectangle

const lastLeft = this.data.left

const lastTop = this.data.top

let left = this.data.left

let top = this.data.top

left = left < (rectangle.windowWidth - rectangle.buttonSize) / 2 ?

rectangle.left : rectangle.right

top = top < rectangle.top + 20 ? rectangle.top :

(top > rectangle.bottom - 20 ? rectangle.bottom : top)

util.playAnimatoin(400, (value) => {

const currentLeft = lastLeft + (left - lastLeft) * value

const currentTop = lastTop + (top - lastTop) * value

this.setData({

left: currentLeft,

top: currentTop

})

})

const position = {

left: left,

top: top

}

util.setStorageSync(POSITION_KEY, position)

}

},

lifetimes: {

attached() {

this.getPosition()

this.getMobile()

}

},

pageLifetimes: {

show() {

this.getPosition()

this.getMobile()

},

resize(size) {

this.getRectangle()

}

},

})

wxss里的make-phone-call-hover样式没有用上,最后没有点击态了。js里的data/touched也没用上。

bug就是,这个浮动按钮可能被拖到上边缘以外,个人觉得尚能接受。目前在模拟器及安卓手机上运行良好。最后来张截图:

ec9d375e1fb3fda7ca507f2f12cbd21b.png

WechatIMG173.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值