前言
很多手机都有悬浮球的功能,并且悬浮球可以有很多功能点,可以显示、隐藏与拖拽,这次我也来实现一个悬浮球的组件
介绍
这是一个悬浮球,我们可以对这个悬浮球进行拖拽,当靠近左右两边时,会自动进行隐藏
效果展示
使用
参数名称 | 参数描述 | 参数类型 | 默认值 |
x | 横坐标 | Number | 100 |
y | 纵坐标 | Number | 100 |
isShow | 是否显示悬浮球 | Boolean | true |
原理分析
1、拖拽原理
获取元素有两种方法:this.refs(“ball”),注意:获取元素不能在onInit()中获取,否则获取不到。
- 在手指触摸事件中,将触摸时的坐标获取并存储,通过e.touches[0].localX获取。
- 手指触摸屏幕移动的事件中,我们再获取移动后的坐标,通过移动后的坐标 - 移动前的坐标,就可以得到偏移量,最后再将当前坐标存储,依次循环。
- 计算得到偏移量后,我们需要获取当前元素距离左边和顶部的距离,将该距离和偏移量相加,就能得到元素的最新位置。
- 将最新的元素位置,通过动态样式赋值给元素,就能实现拖拽了。
// 手指刚触摸屏幕时触发该事件。
mousedownHandler (e) {
this.startPositionX = e.touches[0].localX;
this.startPositionY = e.touches[0].localY;
this.show()
},
// 手指触摸屏幕后移动时触发该事件
mousemoveHandler (e) {
const offsetX = e.touches[0].localX - this.startPositionX;
const offsetY = e.touches[0].localY - this.startPositionY;
const ball = this.$element("ball")
this.left = ball.getBoundingClientRect().left + offsetX
this.top = ball.getBoundingClientRect().top + offsetY
this.startPositionX = e.touches[0].localX;
this.startPositionY = e.touches[0].localY;
},
2、显示与隐藏
显示与隐藏需要配合动画效果会更好!
- 定义显示与隐藏两个事件,当我们触发手指触摸屏幕事件时,调用显示事件;当触发手指触摸结束离开事件时,调用隐藏事件。
- 隐藏事件的触发条件:当元素贴近左侧或者右侧时,就将元素进行隐藏,隐藏就是将元素的一半位置移除屏幕外。
通过判断条件,当元素距离左侧位置left小于0时,就将元素的一半宽度,赋值给left,注意需要设置为负值!当元素贴近右侧,此时需要获取屏幕宽度和元素宽度,因为当元素的右侧贴近屏幕右侧时,即触发隐藏事件,所以需要通过屏幕宽度-元素宽度。
- 显示事件的触发条件:我们只需要判断元素距离左侧位置left是否小于0即可,如果小于零,就让left=0;再判断元素距离右侧是否大于屏幕宽度-元素宽度,如果是,就让元素右侧距离 = 屏幕宽度-元素宽度。
hide(){
const container = this.$element("container")
const ball = this.$element("ball")
const containerWidth = container.getBoundingClientRect().width
const ballWidth = ball.getBoundingClientRect().width
if(this.left<0){
this.left = -ballWidth/2
}else if(this.left > containerWidth-ballWidth){
this.left = containerWidth-ballWidth/2
}
},
show(){
const container = this.$element("container")
const ball = this.$element("ball")
const containerWidth = container.getBoundingClientRect().width
const ballWidth = ball.getBoundingClientRect().width
if(this.left<0){
this.left = 0
}else if(this.left > containerWidth-ballWidth){
this.left = containerWidth-ballWidth
}
}
完整代码
index.js:
// @ts-nocheck
export default {
data: {
// 标记滑块是否显示
isShow: true,
startPositionX: 0,
startPositionY: 0,
top: null,
left: null
},
onInit(){
this.top = 0;
this.left = 0;
},
// 手指刚触摸屏幕时触发该事件。
mousedownHandler (e) {
this.startPositionX = e.touches[0].localX;
this.startPositionY = e.touches[0].localY;
this.show()
},
// 手指触摸屏幕后移动时触发该事件
mousemoveHandler (e) {
const offsetX = e.touches[0].localX - this.startPositionX;
const offsetY = e.touches[0].localY - this.startPositionY;
const ball = this.$element("ball")
this.left = ball.getBoundingClientRect().left + offsetX
this.top = ball.getBoundingClientRect().top + offsetY
this.startPositionX = e.touches[0].localX;
this.startPositionY = e.touches[0].localY;
},
// 手指触摸结束离开屏幕时触发该事件。
mouseupHandler (e) {
this.hide();
},
hide(){
const container = this.$element("container")
const ball = this.$element("ball")
const containerWidth = container.getBoundingClientRect().width
const ballWidth = ball.getBoundingClientRect().width
if(this.left<0){
this.left = -ballWidth/2
}else if(this.left > containerWidth-ballWidth){
this.left = containerWidth-ballWidth/2
}
},
show(){
const container = this.$element("container")
const ball = this.$element("ball")
const containerWidth = container.getBoundingClientRect().width
const ballWidth = ball.getBoundingClientRect().width
if(this.left<0){
this.left = 0
}else if(this.left > containerWidth-ballWidth){
this.left = containerWidth-ballWidth
}
}
}
index.hml:
<div class="container" id="container">
<div
id="ball"
class="floatBall"
style="left: {{left}}px;top: {{top}}px;"
@touchstart="mousedownHandler"
@touchmove="mousemoveHandler"
@touchend="mouseupHandler">
<slot></slot>
</div>
</div>
index.css:
.container {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.floatBall {
position: absolute;
}
总结
不足点:拖拽感觉不流畅,后续还需要优化
最后,通过自定义组件,加深对HarmonyOS的开发,共建鸿蒙生态!
文章相关附件可以点击下面的原文链接前往下载:
https://ost.51cto.com/resource/2218。