| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- <template>
- <view>
- <view
- id="_drag_button"
- class="drag"
- :style="'left: ' + left + 'px; top:' + top + 'px;'"
- @touchstart="touchstart"
- @touchmove.stop.prevent="touchmove"
- @touchend="touchend"
- @click.stop.prevent="click"
- :class="{ transition: isDock && !isMove }"
- >
- <text class="title">{{ text }}</text>
- <text class="price">{{ price }}</text>
- </view>
- </view>
- </template>
- <script>
- export default {
- name: "drag-button",
- props: {
- // 是否开启贴边(拖拽结束后自动贴左右边缘)
- isDock: {
- type: Boolean,
- default: false,
- },
- // 显示的价格数值
- price: {
- type: Number,
- default: 0,
- },
- // 是否存在底部TabBar(存在时需调整屏幕可用高度)
- existTabBar: {
- type: Boolean,
- default: false,
- },
- },
- data() {
- return {
- top: 0, // 按钮顶部距离屏幕顶部的距离
- left: 0, // 按钮左侧距离屏幕左侧的距离
- width: 0, // 按钮宽度
- height: 0, // 按钮高度
- offsetWidth: 0, // 按钮宽度的一半(用于拖拽时定位)
- offsetHeight: 0, // 按钮高度的一半(用于拖拽时定位)
- windowWidth: 0, // 屏幕可用宽度
- windowHeight: 0, // 屏幕可用高度
- isMove: true, // 是否处于拖拽中(控制过渡动画)
- edge: 15, // 按钮与屏幕边缘的间距(左右/上下通用)
- text: "实时金价", // 按钮顶部文本
- };
- },
- mounted() {
- // 获取系统信息(屏幕尺寸、状态栏等)
- const sys = uni.getSystemInfoSync();
- this.windowWidth = sys.windowWidth;
- this.windowHeight = sys.windowHeight;
- // APP端有TabBar时,减去TabBar高度(默认50px,可根据实际调整)
- // #ifdef APP-PLUS
- this.existTabBar && (this.windowHeight -= 50);
- // #endif
- // 处理部分设备顶部状态栏偏移(确保屏幕高度计算完整)
- if (sys.windowTop) {
- this.windowHeight += sys.windowTop;
- }
- // 获取按钮自身的尺寸(宽度/高度)
- const query = uni.createSelectorQuery().in(this);
- query
- .select("#_drag_button")
- .boundingClientRect((data) => {
- this.width = data.width;
- this.height = data.height;
- this.offsetWidth = data.width / 2;
- this.offsetHeight = data.height / 2;
- // 初始left:靠右显示,距离右侧边缘edge距离
- this.left = this.windowWidth - this.width - this.edge;
- // 核心修改:初始top = 屏幕高度的一半 - 按钮高度的一半(实现垂直居中)
- // 确保按钮的垂直中心与屏幕中心对齐,而非按钮顶部对齐屏幕中心
- this.top = this.windowHeight / 2 - this.height / 2;
- })
- .exec();
- },
- methods: {
- // 按钮点击事件(向父组件传参)
- click() {
- this.$emit("btnClick");
- },
- // 触摸开始事件(向父组件传参)
- touchstart(e) {
- this.$emit("btnTouchstart");
- },
- // 触摸移动事件(控制按钮拖拽位置)
- touchmove(e) {
- // 仅允许单指拖拽
- if (e.touches.length !== 1) {
- return false;
- }
- this.isMove = true; // 拖拽中关闭过渡动画
- // 水平方向定位:触摸点X坐标 - 按钮宽度的一半(确保触摸点在按钮中心)
- this.left = e.touches[0].clientX - this.offsetWidth;
- // 垂直方向定位:触摸点Y坐标 - 按钮高度的一半
- let clientY = e.touches[0].clientY - this.offsetHeight;
- // H5端特殊处理:补偿高度偏移(避免拖拽时位置偏移)
- // #ifdef H5
- clientY += this.height;
- // #endif
- // 计算垂直方向边界:底部最大可移动距离(避免按钮超出屏幕底部)
- let edgeBottom = this.windowHeight - this.height - this.edge;
- // 垂直方向边界限制:不允许超出屏幕上下边缘
- if (clientY < this.edge) {
- this.top = this.edge; // 上边界
- } else if (clientY > edgeBottom) {
- this.top = edgeBottom; // 下边界
- } else {
- this.top = clientY; // 正常拖拽位置
- }
- },
- // 触摸结束事件(控制贴边逻辑 + 向父组件传参)
- touchend(e) {
- // 开启贴边功能时,拖拽结束后自动贴左右边缘
- if (this.isDock) {
- const edgeRight = this.windowWidth - this.width - this.edge; // 右侧边缘位置
- // 判断按钮中心是否在屏幕左半区:是则贴左,否则贴右
- if (this.left < this.windowWidth / 2 - this.offsetWidth) {
- this.left = this.edge; // 贴左边缘
- } else {
- this.left = edgeRight; // 贴右边缘
- }
- }
- this.isMove = false; // 拖拽结束开启过渡动画
- this.$emit("btnTouchend"); // 向父组件传参
- },
- },
- };
- </script>
- <style lang="scss">
- .drag {
- display: flex;
- justify-content: center;
- align-items: center;
- flex-direction: column;
- background: linear-gradient(180deg, #fefcf9 0%, #fff2df 100%);
- box-shadow: 0rpx 3rpx 12rpx 0rpx rgba(0, 0, 0, 0.16);
- width: 105upx;
- height: 105upx;
- border-radius: 50%; // 圆形按钮
- position: fixed; // 固定定位(不随页面滚动)
- z-index: 999999; // 最高层级,避免被其他元素遮挡
- .title {
- font-size: 19rpx;
- color: #000000;
- margin-bottom: 4rpx; // 文本与价格间距
- }
- .price {
- font-size: 26rpx;
- color: #cc0000;
- font-weight: bold;
- }
- // 贴边/停止拖拽时的过渡动画
- &.transition {
- transition: left 0.3s ease, top 0.3s ease;
- }
- }
- </style>
|