vue使用Split封装通用拖拽滑动分隔面板组件

前言

手动封装一个类似Iview中的Split组件,可将一片区域,分割为可以拖拽调整宽度或高度的两部分区域,最终效果如下:

开始

基础布局

在vue工程中创建SplitPane组件,引入页面使用。

<template>
 <div class="page">
 <SplitPane />
 </div>
</template>

<script>
import SplitPane from './components/split-pane'
export default {
 components: {
 SplitPane
 },
 data() {
 return {}
 }
}
</script>

<style scoped lang="scss">
.page {
 height: 100%;
 padding: 10px;
 background: #000;
}
</style>
// split-pane.vue

<template>
 <div class="split-pane">
 split
 </div>
</template>

<script>
export default {
 data() {
 return {}
 }
}
</script>

<style scoped lang="scss">
.split-pane {
 background: palegreen;
 height: 100%;
}
</style>

SplitPane组件由三部分组成:区域1,区域2,以及滑动器。添加这三个元素,并分别添加class名,注意.pane为区域1和区域2共用。

<template>
<div class="split-pane">
 <div class="pane pane-one"></div>
 <div class="pane-trigger"></div>
 <div class="pane pane-two"></div>
</div>
</template>

将容器设置为flex布局,区域2的flex属性设为1,则区域2会根据区域1的宽度变化自适应。

<style scoped lang="scss">
.split-pane {
 background: palegreen;
 height: 100%;
 display: flex;
 .pane-one {
 width: 50%;
 background: palevioletred;
 }
 .pane-trigger {
 width: 10px;
 height: 100%;
 background: palegoldenrod;
 }
 .pane-two {
 flex: 1;
 background: turquoise;
 }
}
</style>

可以看到设置区域1的宽度变化就是实现该组件的核心点。

除了横向还要支持纵向布局,所以给组件添加一个direction属性,该属性由外部传入,值为row 或 column,与父元素的flex-direction属性绑定。

<template>
 <div class="split-pane" :style="{ flexDirection: direction }">
 <div class="pane pane-one"></div>
 <div class="pane-trigger"></div>
 <div class="pane pane-two"></div>
 </div>
</template>

<script>
export default {
 props: {
 direction: {
  type: String,
  default: 'row'
 }
 },
 data() {
 return {}
 }
}
</script>

在横向布局中,区域1设置width:50%,滑动器设置width:10px,而变为纵向布局后这两个width应该变为height。所以删除style中这两个width设置,添加一个lengthType计算属性,根据不同的direction在行内样式中给这两个元素分别设置宽高。

<template>
 <div class="split-pane" :style="{ flexDirection: direction }">
 <div class="pane pane-one" :style="lengthType + ':50%'"></div>
 <div class="pane-trigger" :style="lengthType + ':10px'"></div>
 <div class="pane pane-two"></div>
</div>
</template>

computed: {
 lengthType() {
 return this.direction === 'row' ? 'width' : 'height'
 }
}

同时在横向布局中,区域1,区域2,滑动器的height都为100%,在纵向布局下都应该改为width: 100%。所以删除原本的height设置,将direction绑定为容器的一个class,根据该class设置三个子元素两种情况下100%的属性。

<template>
 <div class="split-pane" :class="direction" :style="{ flexDirection: direction }">
 <div class="pane pane-one" :style="lengthType + ':50%'"></div>
 <div class="pane-trigger" :style="lengthType + ':10px'"></div>
 <div class="pane pane-two"></div>
 </div>
</template>

<script>
export default {
 props: {
 direction: {
  type: String,
  default: 'row'
 }
 },
 data() {
 return {}
 },
 computed: {
 lengthType() {
  return this.direction === 'row' ? 'width' : 'height'
 }
 }
}
</script>

<style scoped lang="scss">
.split-pane {
 background: palegreen;
 height: 100%;
 display: flex;
 &.row {
 .pane {
  height: 100%;
 }
 .pane-trigger {
  height: 100%;
 }
 }
 &.column {
 .pane {
  width: 100%;
 }
 .pane-trigger {
  width: 100%;
 }
 }
 .pane-one {
 background: palevioletred;
 }
 .pane-trigger {
 background: palegoldenrod;
 }
 .pane-two {
 flex: 1;
 background: turquoise;
 }
}
</style>

此时如果在页面中给组件传入direction="column",可以看到已经变为纵向

<template>
 <div class="page">
 <SplitPane direction="column" />
 </div>
</template>

数据绑定

当前区域1的宽(高)度和滑动器的宽(高)度都是在样式中写死的,需要变为在js中绑定才能进行操作,首先将能用于计算的数字放在data中

data() {
 return {
  paneLengthPercent: 50, // 区域1宽度 (%)
  triggerLength: 10 // 滑动器宽度 (px)
 }
}

然后通过computed返回两个样式中需要的字符串,同时为了保证滑动器在区域1和区域2的正中间,区域1的宽度应该减去滑动器宽度的一半。

 computed: {
 lengthType() {
  return this.direction === 'row' ? 'width' : 'height'
 },

 paneLengthValue() {
  return `calc(${this.paneLengthPercent}% - ${this.triggerLength / 2 + 'px'})`
 },

 triggerLengthValue() {
  return this.triggerLength + 'px'
 }
 }

最后绑定在模板中

<template>
 <div class="split-pane" :class="direction" :style="{ flexDirection: direction }">
 <div class="pane pane-one" :style="lengthType + ':' + paneLengthValue"></div>
 <div class="pane-trigger" :style="lengthType + ':' + triggerLengthValue"></div>
 <div class="pane pane-two"></div>
 </div>
</template>

事件绑定

想象一下拖拽滑动器的过程,第一步是在滑动器上按下鼠标,给滑动器添加mousedown事件

<div class="pane-trigger" :style="lengthType + ':' + triggerLengthValue" @mousedown="handleMouseDown"></div>

按下鼠标后开始滑动,应该监听mousemove事件,但注意不是在滑动器上,而是在整个文档上监听,因为鼠标有可能滑动到页面任何位置。当用户松开鼠标时,应该取消对整个文档mousemove的监听,所以在鼠标按下的那一刻,应该对document添加两个事件:鼠标移动和鼠标松开

 methods: {
  // 按下滑动器
 handleMouseDown(e) {
  document.addEventListener('mousemove', this.handleMouseMove)
  document.addEventListener('mouseup', this.handleMouseUp)
 },

 // 按下滑动器后移动鼠标
 handleMouseMove(e) {
  console.log('拖动中')
 },

 // 松开滑动器
 handleMouseUp() {
  document.removeEventListener('mousemove', this.handleMouseMove)
 }
 }

我们实际要控制的是区域1的宽度,让区域1的宽度等于当前鼠标距容器左边的距离,也就是如果鼠标移动到下图的圆圈位置,让区域1的宽度等于中间的长度:

这个长度可以根据当前鼠标距页面最左边的距离减去容器距页面最左边的距离算出,也就是绿色长度等于红色减蓝色:

给容器添加ref为了获取容器的dom信息

...
<div ref="splitPane" class="split-pane" :class="direction" :style="{ flexDirection: direction }">
...

如果打印ref的getBoundingClientRect()可以看到如下信息:

console.log(this.$refs.splitPane.getBoundingClientRect())

其中left代表容器距离页面左侧的距离,width代表容器的宽度。
通过鼠标事件对象event的pageX可以获得当前鼠标距页面左侧的距离,则我们要求的鼠标距容器左侧距离就可以算出来了。
最后用这个距离除以容器宽度乘上100,就得到了这个距离的百分比数值,赋值给paneLengthPercent。

 // 按下滑动器后移动鼠标
 handleMouseMove(e) {
  const clientRect = this.$refs.splitPane.getBoundingClientRect()
  const offset = e.pageX - clientRect.left
  const paneLengthPercent = (offset / clientRect.width) * 100

  this.paneLengthPercent = paneLengthPercent
 },

兼容纵向布局。

 // 按下滑动器后移动鼠标
 handleMouseMove(e) {
  const clientRect = this.$refs.splitPane.getBoundingClientRect()
  let paneLengthPercent = 0

  if (this.direction === 'row') {
  const offset = e.pageX - clientRect.left
  paneLengthPercent = (offset / clientRect.width) * 100
  } else {
  const offset = e.pageY - clientRect.top
  paneLengthPercent = (offset / clientRect.height) * 100
  }

  this.paneLengthPercent = paneLengthPercent
 },

优化

此时看上去需求已经完成,但作为一个通用组件还有几个要优化的地方。

优化一 抖动问题

把滑动器宽度设置大一些后可以发现一个抖动问题如下:

在滑动器两侧按下后轻轻移动就会出现大幅偏移,因为现在的计算逻辑始终认为鼠标在滑动器的正中间,没有把滑动器宽度考虑进去。

在dota中定义一个当前鼠标距滑动器左(顶)侧偏移量

 data() {
 return {
  paneLengthPercent: 50, // 区域1宽度 (%)
  triggerLength: 100, // 滑动器宽度 (px)
  triggerLeftOffset: 0 // 鼠标距滑动器左(顶)侧偏移量
 }
 }

这个值等于鼠标距页面左侧距离减去滑动器距页面左侧距离(通过e.srcElement.getBoundingClientRect()),在每次滑动器被按下时进行赋值,也要区分横向/纵向布局:红 - 蓝 = 绿

 // 按下滑动器
 handleMouseDown(e) {
  document.addEventListener('mousemove', this.handleMouseMove)
  document.addEventListener('mouseup', this.handleMouseUp)

  if (this.direction === 'row') {
  this.triggerLeftOffset = e.pageX - e.srcElement.getBoundingClientRect().left
  } else {
  this.triggerLeftOffset = e.pageY - e.srcElement.getBoundingClientRect().top
  }
 },

有了这个triggerLeftOffset,设置区域1的宽度时就应该变成:鼠标距容器左侧距离   减去 鼠标距滑动器左侧的距离(triggerLeftOffset) 再加上滑动器宽度的一半。
这样就相当于把鼠标又定位回了滑动器正中间。

 // 按下滑动器后移动鼠标
 handleMouseMove(e) {
  const clientRect = this.$refs.splitPane.getBoundingClientRect()
  let paneLengthPercent = 0

  if (this.direction === 'row') {
  const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.width) * 100
  } else {
  const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.height) * 100
  }

  this.paneLengthPercent = paneLengthPercent
 },

此时不再有抖动问题

优化二 鼠标样式

鼠标在滑动器上经过时应该改变样式告诉用户可以拖动,分别在横向布局与纵向布局的滑动器css中添加鼠标样式变化。

<style scoped lang="scss">
.split-pane {
 background: palegreen;
 height: 100%;
 display: flex;
 &.row {
 .pane {
  height: 100%;
 }
 .pane-trigger {
  height: 100%;
  cursor: col-resize; // 这里
 }
 }
 &.column {
 .pane {
  width: 100%;
 }
 .pane-trigger {
  width: 100%;
  cursor: row-resize; // 这里
 }
 }
 .pane-one {
 background: palevioletred;
 }
 .pane-trigger {
 background: palegoldenrod;
 }
 .pane-two {
 flex: 1;
 background: turquoise;
 }
}
</style>

优化三 滑动限制

作为一个通用组件,应该向外部提供设置滑动最小与最大距离的限制功能,接收min与max两个props。

 props: {
 direction: {
  type: String,
  default: 'row'
 },

 min: {
  type: Number,
  default: 10
 },

 max: {
  type: Number,
  default: 90
 }
 },

在handleMouseMove加入判断:

 // 按下滑动器后移动鼠标
 handleMouseMove(e) {
  const clientRect = this.$refs.splitPane.getBoundingClientRect()
  let paneLengthPercent = 0

  if (this.direction === 'row') {
  const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.width) * 100
  } else {
  const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.height) * 100
  }

  if (paneLengthPercent < this.min) {
  paneLengthPercent = this.min
  }
  if (paneLengthPercent > this.max) {
  paneLengthPercent = this.max
  }

  this.paneLengthPercent = paneLengthPercent
 }

优化四 面板默认宽度和滑动器宽度

还是作为一个通用组件,面板初始化比例与滑动器宽度应该也由外部使用者决定。
将data中的paneLengthPercent 和 triggerLength转移到props中,从外部接收。

 props: {
 direction: {
  type: String,
  default: 'row'
 },

 min: {
  type: Number,
  default: 10
 },

 max: {
  type: Number,
  default: 90
 },

 paneLengthPercent: {
  type: Number,
  default: 50
 },

 triggerLength: {
  type: Number,
  default: 10
 }
 },
 data() {
 return {
  triggerLeftOffset: 0 // 鼠标距滑动器左(顶)侧偏移量
 }
 },

在页面中则需传入paneLengthPercent,注意paneLengthPercent必须是一个定义在data中的数据,并且要加上.sync修饰符,因为这个值要动态修改。

// page.vue

<template>
 <div class="page">
 <SplitPane direction="row" :paneLengthPercent.sync="paneLengthPercent" />
 </div>
</template>

...
 data() {
 return {
  paneLengthPercent: 30
 }
 }
...

然后在组件中handleMouseMove中通过this.$emit触发事件的方式修改paneLengthPercent值。

 // 按下滑动器后移动鼠标
 handleMouseMove(e) {
  const clientRect = this.$refs.splitPane.getBoundingClientRect()
  let paneLengthPercent = 0

  if (this.direction === 'row') {
  const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.width) * 100
  } else {
  const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.height) * 100
  }

  if (paneLengthPercent < this.min) {
  paneLengthPercent = this.min
  }
  if (paneLengthPercent > this.max) {
  paneLengthPercent = this.max
  }

  this.$emit('update:paneLengthPercent', paneLengthPercent) // 这里
 },

此时组件的要素信息都可以通过外部的props控制了。

优化五 插槽

作为一个容器组件不能添加内容不是等于白费,分别给两个区域添加两个具名插槽。

<template>
 <div ref="splitPane" class="split-pane" :class="direction" :style="{ flexDirection: direction }">
 <div class="pane pane-one" :style="lengthType + ':' + paneLengthValue">
  <slot name="one"></slot>
 </div>

 <div
  class="pane-trigger"
  :style="lengthType + ':' + triggerLengthValue"
  @mousedown="handleMouseDown">
 </div>

 <div class="pane pane-two">
  <slot name="two"></slot>
 </div>
 </div>
</template>

优化六 禁止选中

在拖动过程中,如果区域中有文字内容可能会出现选中文字的情况,给滑动器添加禁止选中效果。

...
 .pane-trigger {
 user-select: none;
 background: palegoldenrod;
 }
...

结束

组件完整代码

保留各背景色仅为了文章展示需要,实际使用中删除

<template>
 <div ref="splitPane" class="split-pane" :class="direction" :style="{ flexDirection: direction }">
 <div class="pane pane-one" :style="lengthType + ':' + paneLengthValue">
  <slot name="one"></slot>
 </div>

 <div
  class="pane-trigger"
  :style="lengthType + ':' + triggerLengthValue"
  @mousedown="handleMouseDown"
 ></div>

 <div class="pane pane-two">
  <slot name="two"></slot>
 </div>
 </div>
</template>

<script>
export default {
 props: {
 direction: {
  type: String,
  default: 'row'
 },

 min: {
  type: Number,
  default: 10
 },

 max: {
  type: Number,
  default: 90
 },

 paneLengthPercent: {
  type: Number,
  default: 50
 },

 triggerLength: {
  type: Number,
  default: 10
 }
 },
 data() {
 return {
  triggerLeftOffset: 0 // 鼠标距滑动器左(顶)侧偏移量
 }
 },
 computed: {
 lengthType() {
  return this.direction === 'row' ? 'width' : 'height'
 },

 paneLengthValue() {
  return `calc(${this.paneLengthPercent}% - ${this.triggerLength / 2 + 'px'})`
 },

 triggerLengthValue() {
  return this.triggerLength + 'px'
 }
 },

 methods: {
 // 按下滑动器
 handleMouseDown(e) {
  document.addEventListener('mousemove', this.handleMouseMove)
  document.addEventListener('mouseup', this.handleMouseUp)

  if (this.direction === 'row') {
  this.triggerLeftOffset = e.pageX - e.srcElement.getBoundingClientRect().left
  } else {
  this.triggerLeftOffset = e.pageY - e.srcElement.getBoundingClientRect().top
  }
 },

 // 按下滑动器后移动鼠标
 handleMouseMove(e) {
  const clientRect = this.$refs.splitPane.getBoundingClientRect()
  let paneLengthPercent = 0

  if (this.direction === 'row') {
  const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.width) * 100
  } else {
  const offset = e.pageY - clientRect.top - this.triggerLeftOffset + this.triggerLength / 2
  paneLengthPercent = (offset / clientRect.height) * 100
  }

  if (paneLengthPercent < this.min) {
  paneLengthPercent = this.min
  }
  if (paneLengthPercent > this.max) {
  paneLengthPercent = this.max
  }

  this.$emit('update:paneLengthPercent', paneLengthPercent)
 },

 // 松开滑动器
 handleMouseUp() {
  document.removeEventListener('mousemove', this.handleMouseMove)
 }
 }
}
</script>

<style scoped lang="scss">
.split-pane {
 background: palegreen;
 height: 100%;
 display: flex;
 &.row {
 .pane {
  height: 100%;
 }
 .pane-trigger {
  height: 100%;
  cursor: col-resize;
 }
 }
 &.column {
 .pane {
  width: 100%;
 }
 .pane-trigger {
  width: 100%;
  cursor: row-resize;
 }
 }
 .pane-one {
 background: palevioletred;
 }
 .pane-trigger {
 user-select: none;
 background: palegoldenrod;
 }
 .pane-two {
 flex: 1;
 background: turquoise;
 }
}
</style>

组件使用示例

保留各背景色仅为了文章展示需要,实际使用中删除

<template>
 <div class="page">
 <SplitPane
  direction="column"
  :min="20"
  :max="80"
  :triggerLength="20"
  :paneLengthPercent.sync="paneLengthPercent"
 >
  <template v-slot:one>
  <div>
   区域一
  </div>
  </template>

  <template v-slot:two>
  <div>
   区域二
  </div>
  </template>

 </SplitPane>
 </div>
</template>

<script>
import SplitPane from './components/split-pane'

export default {
 components: {
 SplitPane
 },
 data() {
 return {
  paneLengthPercent: 30
 }
 }
}
</script>

<style scoped lang="scss">
.page {
 height: 100%;
 padding: 10px;
 background: #000;
}
</style>

到此这篇关于vue使用Split封装通用拖拽滑动分隔面板组件 的文章就介绍到这了,更多相关vue 拖拽滑动分隔面板 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue拖拽组件列表实现动态页面配置功能

    需求描述 最近在做一个后台系统,有一个功能产品需求是页面分为左右两部分,通过右边的组件列表来动态配置左边的页面视图,并且左边由组件拼装起来的视图,可以实现上下拖拽改变顺序,也可以删除. 根据这个需求我做了下面这个demo. 功能分解 右边的组件列表,可以通过拖拽克隆到左边,拖拽结束后右边组件列表数量不会减少 左边的组件可以展开或收起 左边的组件可以上下拖拽,删除,但不可向右边拖拽 左边组件拖拽过程中不改变其展开和收起状态 demo截图 代码地址 vue-draggable-list 代码预览 <

  • vue 动态创建组件的两种方法

    Vue动态创建组件实例并挂载到body 方式一 import Vue from 'vue' /** * @param Component 组件实例的选项对象 * @param props 组件实例中的prop */ export function create(Component, props) { const comp = new (Vue.extend(Component))({ propsData: props }).$mount() document.body.appendChild(c

  • Vue组件Draggable实现拖拽功能

    Draggable为基于Sortable.js的vue组件,用以实现拖拽功能. 具体说明,请参考:学习链接 npm官方演示: vuedraggable特性: 支持触摸设备 支持拖拽和选择文本 支持智能滚动 支持不同列表之间的拖拽 不以jQuery为基础 和视图模型同步刷新 和vue2的国度动画兼容 支持撤销操作 当需要完全控制时,可以抛出所有变化 可以和现有的UI组件兼容 使用 安装: npm install vuedraggable 页面引入: import draggable from 'v

  • vue拖拽组件使用方法详解

    前言 pc端开发需要拖拽组件完成列表的顺序交换,一般移动端的UI组件会包含,但是我在用的iview并没有此功能的组件,于是手写一个,实现起来很简单.效果图如下: 可以拖拽完成新排序,点击某一项可以触发相关事件. 关于拖拽 drag & drop 拖放(Drag 和 drop)是 HTML5 标准的组成部分. 拖拽对象 dataTransfer对象,只能在拖放事件的事件处理程序中访问.重要属性: effectAllowed ( none | copy | copyLink | copyMove |

  • 深入了解Vue动态组件和异步组件

    1.动态组件 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> #app { font-size: 0 } .dynamic-component-demo-tab-button { padding: 6px 10px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1p

  • vue draggable resizable 实现可拖拽缩放的组件功能

    虽然之前适配过旧版组件,但是因为2.0版本原作者对代码进行了重构,原来修改的代码照搬是不可能的了. 所以也就一直没有将 冲突检测 以及 吸附对齐 功能适配到2.0版本,最近正好有时间就适配一下. 新增特征 冲突检测 吸附对齐 默认样式优化 功能预览 项目地址 github.com/gorkys/vue-- 如果喜欢该项目,欢迎 Star 新增Props isConflictCheck Type: Boolean Required: false Default: false 定义组件是否开启冲突检

  • 利用Vue-draggable组件实现Vue项目中表格内容的拖拽排序

    Vue-draggable 的github传送门 : https://github.com/SortableJS/Vue.Draggable 一. 下载依赖包:npm install vuedraggable -S  二. 在需要使用的当前界面引入依赖,注册组件: import draggable from "vuedraggable"; export default { components: { draggable, } 三. 在template 中建立表格,分别写出thead 部

  • Vue拖拽组件开发实例详解

    为什么选择Vue? 主要原因:对于前端开发来说,兼容性是我们必须要考虑的问题之一.我们的项目不需要兼容低版本浏览器.项目本身也是一个数据驱动型的.加之,Vue本身具有以下主要特性: •使用虚拟DOM: •轻量级框架: •高效的数据绑定: •灵活的组件系统: •完整的开发生态链. 这就是我们为什么选择Vue框架的一些原因. 为什么要封装成一个Vue组件? 主要目的是可提高代码的复用性和可维护性. •复用性:组件化后,一些样式和逻辑均通过配置参数的方式去差异化体现,所以参数的可配置性提高了组件的复用

  • vue3的动态组件是如何工作的

    在这篇文章中,阿宝哥将介绍 Vue 3 中的内置组件 -- component,该组件的作用是渲染一个 "元组件" 为动态组件.如果你对动态组件还不了解的话也没关系,文中阿宝哥会通过具体的示例,来介绍动态组件的应用.由于动态组件内部与组件注册之间有一定的联系,所以为了让大家能够更好地了解动态组件的内部原理,阿宝哥会先介绍组件注册的相关知识. 一.组件注册 1.1 全局注册 在 Vue 3.0 中,通过使用 app 对象的 component 方法,可以很容易地注册或检索全局组件.com

  • vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

    早就实现了功能,但是发现点击的时候,选中的菜单项背景色会变白,周五时候仔细观察了一下,发现并不是调整样式的问题,而是选项没有被选中,于是好好研究了一下组件递归这块,总结记录一下心路历程 一.概念 递归:递归其实说白了,就是自己调用自己,样子就像是套娃一个套一个的,小时候玩过一个游戏汉诺塔就是利用的递归原理: 函数递归:函数利用函数名还调用自己 组件递归:所以组件递归利用的是vue组件中的name属性来实现的 二.需求 实现可折叠动态渲染多级侧边栏导航 三.分析 1.观察到侧边栏导航是一级一级的,

  • vue拖拽组件 vuedraggable API options实现盒子之间相互拖拽排序

    vue拖拽克隆clone组件API, vue.draggable实现盒子之间相互拖拽排序克隆(网上资源整理的文档) 效果图: -------------------------------------------------------------------------------- 首先需要安装vuedraggable依赖包: npm install vuedraggable --save 因为拖拽组件依赖sortablejs ,如果项目没有安装sortablejs ,可能需要安装一下 np

  • vue开发拖拽进度条滑动组件

    分享一个最近写的进度条滑动组件,以前都是用jq写,学会了vue,尝试着拿vue来写觉得非常简单,代码复用性很强! 效果图如下: 调用组件如下: <slider :min=0 :max=100 v-model = "per"></slider> <template> <div class="slider" ref="slider"> <div class="process"

  • vue 实现拖拽动态生成组件的需求

    产品需求 开完产品需求会议,遇到了一个需求,首先页面分成两栏布局,左侧展示数据组件,支持拖拽排序,点击按钮清除组件.右侧支持将组件的缩略图拖拽至左侧生成一个新的组件. 思路 对于动态生成组件来说每一次都要是生成全新的一个组件,那么就可以把 组件放进函数当中 return.在JSX中调用函数,每次调用函数都会返回一个全新的组件.这对React来说非常简单,但是对于Vue来说,直接将组件返回是不可能的.尽管这个 return 写法不适合Vue,但是我们不可否认,思路是非常正确的,所以我们应该考虑一个

随机推荐