#夏日挑战赛# HarmonyOS 实现一个悬浮球 原创 精华

lxj29
发布于 2022-7-25 14:15
浏览
3收藏

本文正在参加星光计划3.0–夏日挑战赛

前言

很多手机都有悬浮球的功能,并且悬浮球可以有很多功能点,可以显示、隐藏与拖拽,这次我也来实现一个悬浮球的组件

介绍

这是一个悬浮球,我们可以对这个悬浮球进行拖拽,当靠近左右两边时,会自动进行隐藏

效果展示

#夏日挑战赛# HarmonyOS 实现一个悬浮球-鸿蒙开发者社区

使用

参数名称 参数描述 参数类型 默认值
x 横坐标 Number 100
y 纵坐标 Number 100
isShow 是否显示悬浮球 Boolean true

原理分析

1.拖拽原理

获取元素有两种方法:this.$element(“ball”)与his.$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的开发,共建鸿蒙生态!

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
源代码.zip 346.62K 24次下载
已于2022-7-29 19:32:01修改
8
收藏 3
回复
举报
4条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

有人能分享下哪些app的悬浮球用的还不错吗?用过的悬浮球感觉都不太方便。

已于2022-7-26 10:16:48修改
回复
2022-7-25 15:37:34
阿毛0920
阿毛0920

6666

 

回复
2022-7-25 16:04:16
lxj29
lxj29 回复了 红叶亦知秋
有人能分享下哪些app的悬浮球用的还不错吗?用过的悬浮球感觉都不太方便。

好像好多手机都有自带的

回复
2022-7-25 17:19:15
lxj29
lxj29 回复了 阿毛0920
6666

999999

回复
2022-7-25 17:19:25
回复
    相关推荐