记录一次vue2项目中拖动div时,div的高度会随着鼠标向上拖动变高

拖动组件:

<template>

    <div

      ref="file-list-win"

      class="file-list-win"

      v-draggable="{limit: true, left: 256, top: 0  }"

      :class="{ min: min == true }"

      style="bottom: 50px"

    >

    <!-- 注意: 如果有拖动的要求,bottom最好不要写在样式文件中,直接写在div的style中,拖动时会清除bottom样式 -->

        <!-- 头部 -->

        <div class="title dragTitle">

            <div>

                <i v-if="getIfAllDone == 1" class="el-icon-success success"></i>

                <div v-else class="loading">

                  <img :src="require('@/assets/images/management/file/loading.png')">

                </div>

            </div>

            <div class="msg">

                <p v-if="getIfAllDone == 1">上传 成功(<span class="success-color">{{ getSuccessCount }}</span>项) 失败(<span class="fail-color">{{ getFailCount }}</span>项)</p>

                <p v-else>正在上传 - 剩余 {{ fileList.length - getDoneCount }} 项</p>

            </div>

            <div class="btns">

                <el-button type="info" circle @click="close()">

                  <i class="el-icon-close"></i>

                </el-button>

                <el-button type="primary" circle v-if="!min" @click="clickSlide()">

                    <i class="el-icon-arrow-up"></i>

                </el-button>

                <el-button type="info" circle v-if="min" @click="clickSlide()">

                    <i class="el-icon-arrow-down"></i>

                </el-button>

            </div>

        </div>

        <div class="content">

            <ul>

                <li

                    v-for="(file, index) in fileList"

                    :key="`item.taskId-${index}`"

                >

                    <div class="bg" v-if="file.status == 'loading' && file.progress > 0" :style="`width: ${file.progress * 100}%`"></div>

                    <div class="bg" v-else style="width: 0"></div>

                    <div class="info">

                        <div class="tb">

                            <template v-if="file.status == 'loading'">

                              <img :src="require('@/assets/images/management/file/file.png')">

                            </template>

                            <template v-else>

                              <img v-if="file.ext =='mp4'" :src="require('@/assets/images/management/file/video.png')"/>

                              <img v-else-if="file.ext =='mp3'" :src="require('@/assets/images/management/file/video.png')"/>

                              <img v-else-if="file.ext =='docx' || file.ext =='doc'" :src="require('@/assets/images/management/file/word.png')"/>

                              <img v-else-if="file.ext =='pptx' || file.ext =='ppt'" :src="require('@/assets/images/management/file/file.png')"/>

                              <img v-else-if="file.ext =='xlsx' || file.ext =='xls'" :src="require('@/assets/images/management/file/excel.png')"/>

                              <img v-else-if="file.ext =='pdf'" :src="require('@/assets/images/management/file/pdf.png')"/>

                              <img v-else :src="require('@/assets/images/management/file/file.png')"/>

                            </template>

                        </div>

                        <div class="msg">

                            <p class="name">{{ file.name }}</p>

                            <p class="other">

                              <span>{{ file.size }}MB</span>

                              <span class="success" v-if="file.status=='success'">上传成功</span>

                              <span class="fail" v-if="file.status=='fail'">上传失败</span>

                            </p>

                        </div>

                    </div>

                </li>

            </ul>

        </div>

    </div>

</template>

<script>

import { mapState } from  'vuex';

export default {

    name: 'FileList',

    components: {},

    props: {

      fileList: {

        type: Array,

        require: true

      }

    },

    data () {

        return {

            min: false,

            height: 0   //存储div本身的高度,展开时计算用到

        };

    },

    computed: {

        // 计算完成的数量

        getDoneCount () {

            return this.fileList.filter(

                (item) => item.status == 'success' || item.status == 'fail'

            ).length;

        },

        // 计算全部导出任务是否完成

        getIfAllDone () {

            let doneCount = this.getDoneCount;

            return doneCount == this.fileList.length ? 1 : 2;

        },

        // 计算成功的数量

        getSuccessCount () {

            return this.fileList.filter(

                (item) => item.status == 'success'

            ).length;

        },

        // 计算失败的数量

        getFailCount () {

            return this.fileList.filter(

                (item) => item.status == 'fail'

            ).length;

        },

    },

    watch: {},

    mounted () {

    },

    methods: {

        // 收起展开

        clickSlide () {

          this.min = !this.min;

          if (!this.min) {

            // 展开

            if (this.$refs['file-list-win'].style.top) {

              // 移动过div

              let top = this.$refs['file-list-win'].style.top;

              let topValue = 0;

              if (top.indexOf('px') > -1) {

                let temp = top.substring(0, top.indexOf('px'));

                topValue = parseInt(temp);

              } else {

                topValue = parseInt(top);

              }

              let h1 = window.innerHeight;

              if (topValue + this.height > h1) {

                // 如果div本身的高度+离顶部的高度 大于 页面的高度,则重新计算div离顶部的高度

                let temp_top = h1 - this.height - 50;

                this.$refs['file-list-win'].style.top = temp_top+'px';

              }

            } else {

              // 未移动过div,不处理

            }

          } else {

            // 收起的时候存下div的高度

            this.height = this.$refs['file-list-win'].offsetHeight;

          }

        },

        // 关闭

        close () {

            if (this.getIfAllDone == 1) {

                this.$emit('closeFileList');

            } else {

                this.$message({

                    type: "warning",

                    message: "有正在进行的任务,请勿关闭!",

                });

            }

        },

    }

};

</script>

<style lang="scss" scoped>

.file-list-win {

    position: fixed;

    right: vw(15);

    // bottom: 50px

    width: vw(400);

    border-radius: 6px;

    z-index:999;

    background: #ffffff;

    box-shadow: rgba(0, 0, 0, 0.1) 0px 3px 5px, rgba(0, 0, 0, 0.06) 0px 6px 10px,

        rgba(0, 0, 0, 0.06) 0px 1px 16px;

    overflow: hidden;

    .title {

        display: flex;

        padding: vh(16) vw(24);

        line-height: vh(24);

        border-bottom: 1px solid #e9e9e9;

        .success {

            color: #66b165;

            font-size: 22px;

            margin-top: 2px;

        }

        .loading {

            width: vw(30);

            height: vw(30);

            img {

              position: relative;

              top: -2px;

              width: 100%;

              height: 100%;

            }

        }

        .msg {

            flex: 1;

            margin-left: vw(10);

            .total {

                color: #909399;

            }

        }

        .btns {

            margin-top: -1px;

            .el-button--medium.is-circle {

                padding: 2px;

            }

            i {

                cursor: pointer;

                font-size: 20px;

            }

        }

    }

    .content {

        max-height: vh(380);

        overflow: auto;

        ul {

            padding: 0;

            li {

                position: relative;

                margin-bottom: vh(10);

                .bg {

                  height: vh(70);

                  background: #F5F5F6;

                  border-bottom: 2px solid #1890FF;

                  z-index: 0;

                }

                .info {

                    position: absolute;

                    left: 0;

                    top: 0;

                    z-index: 1;

                    padding: vh(10) vw(20) vh(10) vw(30);

                    height: vh(50);

                    display: flex;

                    margin-bottom: vh(10);

                    // &:hover {

                    //     background-color: #f5f6f6;

                    // }

                    .tb {

                        width: vw(40);

                        height: vw(40);

                        line-height: vw(40);

                        background: #EAF5FF;

                        border-radius: 6px;

                        text-align: center;

                    }

                    .msg {

                        margin-left: vw(10);

                        flex: 1;

                        .name {

                            width: vw(240);

                            height: vh(30);

                            word-wrap: break-word; /*强制换行*/

                            overflow: hidden; /*超出隐藏*/

                            text-overflow: ellipsis; /*隐藏后添加省略号*/

                            white-space: nowrap; /*强制不换行*/

                            margin: 0;

                        }

                        .other {

                            color: #999999;

                            margin: 0;

                            .success {

                                color: #20A80D;

                                margin-left: vw(10);

                            }

                            .fail {

                                color: red;

                                margin-left: vw(10);

                            }

                        }

                    }

                    .btns {

                        i {

                            cursor: pointer;

                            margin-left: vw(10);

                        }

                        .percent {

                            margin-left: vw(10);

                        }

                    }

                }

            }

        }

    }

    .success-color {

      color: #20A80D;

    }

    .fail-color {

      color: red;

    }

}

.min {

    height: vh(60);

    .content {

        display: none;

    }

}

</style>

拖动指令:

export default {

  install (Vue) {

        // 拖动指令,绑定在div上,

        // 被绑定的div的css样式不要有bottom属性,如果初始化需要用到bottom定位,请直接写在div的style中

        // binding 传值可选 {limit: true, left: 256, top: 0 }

        // limit - 有范围限制,如传入false,可能会拖到父级DOM元素外

        // left - 拖动div离父级DOM左侧最小距离

        // top - 拖动div离父级DOM上侧侧最小距离

        Vue.directive('draggable', {

          // 当被绑定的元素挂载到 DOM 上时...

          bind (el, binding, vnode) {

            const dragTitleEl = el.querySelector('.dragTitle'); // 获取弹窗标题栏元素

            dragTitleEl.style.cssText += ';cursor:move;'

            console.log('binding: ', binding);

            // 添加鼠标按下事件监听

            el.onmousedown = function(e) {

              // 获取鼠标点击位置相对于元素左上角的x,y坐标

              let disX = e.clientX - el.offsetLeft;

              let disY = e.clientY - el.offsetTop;

              // 添加鼠标移动事件监听

              document.onmousemove = function(e) {

                // 计算元素的新位置

                let l = e.clientX - disX;

                let t = e.clientY - disY;

                // 限制拖拽范围

                if (binding.value.limit) {

                  let limit_l = binding.value.left || 0;

                  let limit_t = binding.value.top || 0;

                  if (l < limit_l) {

                    l = limit_l;

                  }

                  if (t < limit_t) {

                    t = limit_t;

                  }

                  if (l > window.innerWidth - el.offsetWidth) {

                    l = window.innerWidth - el.offsetWidth;

                  }

                  if (t > window.innerHeight - el.offsetHeight) {

                    t = window.innerHeight - el.offsetHeight;

                  }

                }

                // 清空之前的样式

                el.style.cssText = "";

                // 设置元素的位置

                el.style.left = l + 'px';

                el.style.top = t + 'px';

              };

              // 添加鼠标抬起事件监听,用于停止拖拽

              document.onmouseup = function(e) {

                document.onmousemove = null;

                document.onmouseup = null;

              };

            };

          }

        })

    }

}

出现错误的原因:

由于最初要求div是定位到右下角,所以使用了bottom属性(且写在样式文件中),在拖动计算过程中,只计算left 和 top属性,所以向上拖动时,由于bottom属性一直存在,div离下边缘的距离不变,但是left属性使div离上边缘的距离变了,就出现了把div高度拖高的问题。

解决方法:

1、bottom直接写在div的style中;

2、拖动过程中使用el.style.cssText = "";  清空样式。

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现 div 横向拖动并互换位置,可以使用 Vue.js 和一些 JavaScript 库来实现。 首先,你需要在 Vue.js 创建一个拖动事件。你可以使用 `v-on:mousemove` 来监听鼠标移动事件,并使用 `v-on:mousedown` 来监听鼠标按下事件。在鼠标按下事件,你需要记录鼠标的当前位置。 接下来,在鼠标移动事件,你需要计算鼠标的偏移量,并将偏移量添加到 div 的 left 样式。这将使 div 沿着 x 轴移动。 当鼠标释放,你需要计算 div 的位置,并使用 JavaScript 库来实现 div 位置的互换。你可以使用 Sortable.js 或者 Draggable.js 这样的库来实现拖拽和位置交换。 最后,你需要将这些逻辑封装在一个 Vue.js 组件,以便在应用程序使用。以下是一个简单的示例: ```html <template> <div class="container"> <div v-for="(item, index) in items" :key="index" :class="{ active: activeIndex === index }" v-bind:style="{ left: item.left + 'px' }" v-on:mousedown="startDrag(index, $event)"> {{ item.text }} </div> </div> </template> <script> import Sortable from 'sortablejs'; export default { data() { return { items: [ { text: 'Item 1', left: 0 }, { text: 'Item 2', left: 100 }, { text: 'Item 3', left: 200 }, { text: 'Item 4', left: 300 }, ], activeIndex: null, startX: 0, }; }, mounted() { const container = this.$el.querySelector('.container'); Sortable.create(container, { animation: 150, swapThreshold: 0.5, onSwap: (evt) => { const { oldIndex, newIndex } = evt; const item = this.items.splice(oldIndex, 1)[0]; this.items.splice(newIndex, 0, item); }, }); }, methods: { startDrag(index, event) { this.activeIndex = index; this.startX = event.clientX; window.addEventListener('mousemove', this.drag); window.addEventListener('mouseup', this.stopDrag); }, drag(event) { const deltaX = event.clientX - this.startX; this.items[this.activeIndex].left += deltaX; this.startX = event.clientX; }, stopDrag() { window.removeEventListener('mousemove', this.drag); window.removeEventListener('mouseup', this.stopDrag); this.activeIndex = null; }, }, }; </script> <style> .container { display: flex; position: relative; width: 100%; height: 100px; background-color: #f5f5f5; } .container > div { position: absolute; top: 0; height: 100%; width: 100px; background-color: #fff; border: 1px solid #ccc; display: flex; justify-content: center; align-items: center; cursor: move; } .container > div.active { z-index: 1; } </style> ``` 在这个示例,我们创建了一个包含多个 div 的容器,并使用 Vue.js 的数据绑定将每个 div 的位置绑定到数据模型。当用户按下鼠标并开始拖动 div ,我们记录 div 的当前位置,并在每次移动计算偏移量。当用户释放鼠标,我们使用 Sortable.js 库来实现 div 的位置交换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值