手写可拖动穿梭框组件CustormTransfer vue实现示例

目录
  • 本文内容
  • 最终效果图
  • 组件html布局
  • 穿梭框左侧内容
  • 穿梭框右侧内容
  • 穿梭框中间向左、向右按钮
  • 把排序好的穿梭数据传给父组件
  • 整体代码
  • 小结

本文内容

需求是实现类似 el-transfer的组件,右侧框内容可以拖动排序;

手写div样式 + vuedraggable组件实现。

最终效果图

组件html布局

新建一个组件文件 CustormTransfer.vue,穿梭框 html 分为左中右三部分,使用flex布局使其横向布局,此时代码如下

<template>
  <div class="custom-transfer-cls">
    <div class="left-side"></div>
    <div class="btn-cls"></div>
    <div class="right-side"></div>
  </div>
</template>
<script>
export default {
  name: 'CustomTransferName',
  components: {},
  props: {},
  data () {
    return { }
  },
  computed: { },
  created () {},
  mounted () { },
  methods: {}
}
</script>
<style lang="less" scoped>
.custom-transfer-cls {
  display: flex;
  justify-content: space-between;
  min-height: 120px;
  .left-side,
  .right-side {}
  .btn-cls { }
}
</style>

此时页面上看不到组件内容。

穿梭框左侧内容

左侧内容是个列表,列表的每一项是多选框checkbox加文字标题,列表最上面是标题;所以.left-side的代码如下:

<div class="left-side">
  <!-- 标题 -->
  <h4>{{ titles[0] }}</h4>
  <!-- 列表 -->
  <div v-for="left in leftData" :key="left.key" class="item-cls">
    <el-checkbox :checked="left.checked" @change="leftCheckChange(left)" />
    <span :title="left.label">{{ left.label }}</span>
  </div>
  <!-- 数据为空时显示 -->
  <div v-if="leftData.length === 0" class="empty-text">{{ emptypText }}</div>
</div>

解析:

  • 列表标题使用h4标签,titles是组件使用者传入props的标题数组的第一项;
  • 列表数据 leftData是组件使用者传入的数据处理之后的,因为我们默认el-checkbox不勾选,所以在生命周期mounted时,checked设为false;
  • el-checkbox触发change事件时,执行函数leftCheckChange(left),去改变leftData数组对应项的checked设为取反;
  • leftData数据为空时,显示数据为空的文本,此文本组件使用者可通过 属性 emptypText 传入,默认'数据为空';
  • 列表的每一项的样式在 .item-cls 定义,内容过长时显示省略号,在 title 属性中显示全部内容;
  • 列表整体内容多时,显示滚动条,滚动条样式重写;

以上内容加上样式、函数后如下:

<template>
  <div class="custom-transfer-cls">
    <div class="left-side"></div>
    <div class="btn-cls"></div>
    <div class="right-side"></div>
  </div>
</template>
<script>
export default {
  name: 'CustomTransferName',
  components: {},
  props: {
    allData: {
      type: Array,
      default: () => {
        // 对象数组需要有label、key两个属性
        return []
      }
    },
    emptypText: {
      type: String,
      default: '数据为空'
    },
    titles: {
      type: Array,
      default: () => {
        return ['列表 1', '列表 2']
      }
    }
  },
  data () {
    return {
      leftData: []
    }
  },
  computed: { },
  created () {},
  mounted () {
    // 初始化列表1的数据
    this.leftData = this.allData.map(a => {
      a.checked = false
      return a
    })
  },
  methods: {
    // 左边checkbox的change事件
    leftCheckChange (check) {
      this.leftData = this.leftData.map(l => {
        if (l.key === check.key) {
          l.checked = !l.checked
        }
        return l
      })
    }
  }
}
</script>
<style lang="less" scoped>
.custom-transfer-cls {
  display: flex;
  justify-content: space-between;
  min-height: 120px;
  .left-side {
    height: 240px;
    overflow-y: scroll;
    background-color: white;
    width: 140px;
    border: 1px solid #eee;
    border-radius: 4px;
    h4 {
      /* 列表标题在列表滚动时吸附在顶部 */
      position: sticky;
      top: 0px;
      z-index: 9;
      background: white;
      text-align: center;
      font-weight: 400;
      margin-bottom: 16px;
    }
    /* 数据为空的样式 */
    .empty-text {
      text-align: center;
      color: #ccc;
    }
    /* 列表每项的样式,文字很长时显示省略号 */
    .item-cls {
      margin-left: 12px;
      margin-right: 12px;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
    /* 列表的滚动条样式重写 */
    &::-webkit-scrollbar {
      width: 1px;
    }
    &::-webkit-scrollbar-thumb {
      background: #ccc;
    }
    &::-webkit-scrollbar-track {
      background: #ededed;
    }
  }
  .btn-cls { }
}
</style>

穿梭框右侧内容

右侧的列表需要具有可拖动排序的功能,我使用的使 vuedraggable组件,所以首先需要先安装npm install vuedraggable -S, 再引入 import draggable from 'vuedraggable',使用时配合 <transition-group>增加过渡效果;代码如下:

<div class="right-side">
  <h4>{{ titles[1] }}</h4>
  <draggable v-model="rightData">
    <transition-group>
      <div v-for="(right, index) in rightData" :key="right.key" class="item-cls">
        <el-checkbox :checked="right.checked" @change="rightCheckChange(right)" />
        <span>{{ index + 1 + '.' }}</span>
        <span :title="right.label">{{ right.label }}</span>
      </div>
    </transition-group>
  </draggable>
  <div v-if="rightData.length === 0" class="empty-text">{{ emptypText }}</div>
</div>

解析:

  • 右侧的列表样式和左侧一样;
  • 只是多了一个<draggable></draggable>组件的使用

此时整体的代码如下:

<template>
  <div class="custom-transfer-cls">
    <!-- 左侧列表 -->
    <div class="left-side">
      <h4>{{ titles[0] }}</h4>
      <div v-for="left in leftData" :key="left.key" class="item-cls">
        <el-checkbox :checked="left.checked" @change="leftCheckChange(left)" />
        <span :title="left.label">{{ left.label }}</span>
      </div>
      <div v-if="leftData.length === 0" class="empty-text">{{ emptypText }}</div>
    </div>
    <!-- 向左、向右操作按钮 -->
    <div class="btn-cls"></div>
    <!-- 右侧列表 -->
    <div class="right-side">
      <h4>{{ titles[1] }}</h4>
      <draggable v-model="rightData">
        <transition-group>
          <div v-for="(right, index) in rightData" :key="right.key" class="item-cls">
            <el-checkbox :checked="right.checked" @change="rightCheckChange(right)" />
            <span>{{ index + 1 + '.' }}</span>
            <span :title="right.label">{{ right.label }}</span>
          </div>
        </transition-group>
      </draggable>
      <div v-if="rightData.length === 0" class="empty-text">{{ emptypText }}</div>
    </div>
  </div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
  name: 'CustomTransferName',
  components: {
    draggable
  },
  props: {
    allData: {
      type: Array,
      default: () => {
        // 对象数组需要有label、key两个属性
        return []
      }
    },
    checkedData: {
      type: Array,
      default: () => {
        // 对象数组需要有label、key两个属性
        return []
      }
    },
    emptypText: {
      type: String,
      default: '数据为空'
    },
    titles: {
      type: Array,
      default: () => {
        return ['标题1', '标题2']
      }
    }
  },
  data () {
    return {
      leftData: [],
      rightData: []
    }
  },
  computed: {},
  created () {},
  mounted () {
    // 初始化左侧列表1的数据
    this.leftData = this.allData.map(a => {
      a.checked = false
      return a
    })
    // 初始化右侧列表2的数据
    this.rightData = this.checkedData.map(a => {
      a.checked = false
      return a
    })
  },
  methods: {
    // 左边选中
    leftCheckChange (check) {
      this.leftData = this.leftData.map(l => {
        if (l.key === check.key) {
          l.checked = !l.checked
        }
        return l
      })
    },
    // 右边选中
    rightCheckChange (check) {
      this.rightData = this.rightData.map(l => {
        if (l.key === check.key) {
          l.checked = !l.checked
        }
        return l
      })
    }
  }
}
</script>
<style lang="less" scoped>
.custom-transfer-cls {
  display: flex;
  justify-content: space-between;
  min-height: 120px;
  .left-side,
  .right-side {
    height: 240px;
    overflow-y: scroll;
    background-color: white;
    width: 140px;
    border: 1px solid #eee;
    border-radius: 4px;
    h4 {
      /* 列表标题在列表滚动时吸附在顶部 */
      position: sticky;
      top: 0px;
      z-index: 9;
      background: white;
      text-align: center;
      font-weight: 400;
      margin-bottom: 16px;
    }
    /* 数据为空的样式 */
    .empty-text {
      text-align: center;
      color: #ccc;
    }
    /* 列表每项的样式,文字很长时显示省略号 */
    .item-cls {
      margin-left: 12px;
      margin-right: 12px;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
    /* 列表的滚动条样式重写 */
    &::-webkit-scrollbar {
      width: 1px;
    }
    &::-webkit-scrollbar-thumb {
      background: #ccc;
    }
    &::-webkit-scrollbar-track {
      background: #ededed;
    }
  }
}
</style>

穿梭框中间向左、向右按钮

穿梭框的向左、向右按钮,使用<el-button icon="el-icon-arrow-right"></el-button>实现,代码如下:

<div class="btn-cls">
  <el-button
    :disabled="toRightDisable"
    plain
    type="default"
    size="small"
    icon="el-icon-arrow-right"
    @click="toRight"
  />
  <el-button
    :disabled="toLeftDisable"
    class="right-btn"
    plain
    type="default"
    size="small"
    icon="el-icon-arrow-left"
    @click="toLeft"
  />
</div>

解析:

  • 按钮的禁用disabled逻辑,在computed中定义toRightDisable、toLeftDisable
  • 按钮的点击事件 toRight、toLeft,是对左右两侧列表数组的运算;

此部分的代码如下:

<template>
  <div class="custom-transfer-cls">
    <div class="left-side"></div>
    <!-- 向左、向右按钮开始 -->
    <div class="btn-cls">
      <el-button
        :disabled="toRightDisable"
        plain
        type="default"
        size="small"
        icon="el-icon-arrow-right"
        @click="toRight"
      />
      <el-button
        :disabled="toLeftDisable"
        class="right-btn"
        plain
        type="default"
        size="small"
        icon="el-icon-arrow-left"
        @click="toLeft"
      />
    </div>
    <!-- 向左、向右按钮结束 -->
    <div class="right-side"></div>
  </div>
</template>
<script>
export default {
  name: 'CustomTransferName',
  components: { },
  props: {},
  data () {
    return {
      leftData: [],
      rightData: []
    }
  },
  computed: {
    // 向左穿梭按钮的disabled逻辑
    toLeftDisable () {
      return !this.rightData.some(r => r.checked)
    },
    // 向右穿梭按钮的disabled逻辑
    toRightDisable () {
      return !this.leftData.some(r => r.checked)
    }
  },
  created () {},
  mounted () { },
  methods: {
    // 数据向右穿梭
    toRight () {
      // 左减去,右加上
      const leftUnchecked = this.leftData.filter(l => !l.checked)
      const leftChecked = this.leftData.filter(l => l.checked)
      this.leftData = leftUnchecked
      this.rightData = [].concat(this.rightData, leftChecked).map(r => {
        r.checked = false
        return r
      })
    },
    // 数据向左穿梭
    toLeft () {
      // 右减去,左加上
      const rightUnchecked = this.rightData.filter(l => !l.checked)
      const rightChecked = this.rightData.filter(l => l.checked)
      this.rightData = rightUnchecked
      this.leftData = [].concat(this.leftData, rightChecked).map(r => {
        r.checked = false
        return r
      })
    }
  }
}
</script>
<style lang="less" scoped>
.custom-transfer-cls {
  display: flex;
  justify-content: space-between;
  min-height: 120px;
  .btn-cls {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .right-btn {
      margin-left: 0;
      margin-top: 8px;
    }
  }
}
</style>

把排序好的穿梭数据传给父组件

即把rightData: []数据通过$emit()传递出去,父组件监听dragedData事件之后获取; 定义函数 transferData(),在拖动完成时的@end事件调用,在向左向右更新了右侧列表数据之后调用;

代码如下:

methods: {
  // 传递数据
  transferData () {
    this.$emit('dragedData', this.rightData)
  }
}

整体代码

<template>
  <div class="custom-transfer-cls">
    <!-- 左侧列表 -->
    <div class="left-side">
      <h4>{{ titles[0] }}</h4>
      <div v-for="left in leftData" :key="left.key" class="item-cls">
        <el-checkbox :checked="left.checked" @change="leftCheckChange(left)" />
        <span :title="left.label">{{ left.label }}</span>
      </div>
      <div v-if="leftData.length === 0" class="empty-text">{{ emptypText }}</div>
    </div>
    <!-- 向左、向右按钮开始 -->
    <div class="btn-cls">
      <el-button
        :disabled="toRightDisable"
        plain
        type="default"
        size="small"
        icon="h-icon-angle_right"
        @click="toRight"
      />
      <el-button
        :disabled="toLeftDisable"
        class="right-btn"
        plain
        type="default"
        size="small"
        icon="h-icon-angle_left"
        @click="toLeft"
      />
    </div>
    <!-- 右侧列表 -->
    <div class="right-side">
      <h4>{{ titles[1] }}</h4>
      <draggable v-model="rightData" @end="transferData">
        <transition-group>
          <div v-for="(right, index) in rightData" :key="right.key" class="item-cls">
            <el-checkbox :checked="right.checked" @change="rightCheckChange(right)" />
            <span>{{ index + 1 + '.' }}</span>
            <span :title="right.label">{{ right.label }}</span>
          </div>
        </transition-group>
      </draggable>
      <div v-if="rightData.length === 0" class="empty-text">{{ emptypText }}</div>
    </div>
  </div>
</template>
<script>
// 可拖动组件
import draggable from 'vuedraggable'
export default {
  name: 'CustomTransferName',
  components: {
    draggable
  },
  props: {
    allData: {
      type: Array,
      default: () => {
        // 对象数组需要有label、key两个属性
        return []
      }
    },
    checkedData: {
      type: Array,
      default: () => {
        // 对象数组需要有label、key两个属性
        return []
      }
    },
    emptypText: {
      type: String,
      default: '数据为空'
    },
    titles: {
      type: Array,
      default: () => {
        return ['标题1', '标题2']
      }
    }
  },
  data () {
    return {
      leftData: [],
      rightData: []
    }
  },
  computed: {
    // 向左穿梭按钮的disabled逻辑
    toLeftDisable () {
      return !this.rightData.some(r => r.checked)
    },
    // 向右穿梭按钮的disabled逻辑
    toRightDisable () {
      return !this.leftData.some(r => r.checked)
    }
  },
  created () {},
  mounted () {
    // 初始化左侧列表1的数据
    this.leftData = this.allData.map(a => {
      a.checked = false
      return a
    })
    // 初始化右侧列表2的数据
    this.rightData = this.checkedData.map(a => {
      a.checked = false
      return a
    })
  },
  methods: {
    // 传递数据
    transferData () {
      this.$emit('dragedData', this.rightData)
    },
    // 左边选中
    leftCheckChange (check) {
      this.leftData = this.leftData.map(l => {
        if (l.key === check.key) {
          l.checked = !l.checked
        }
        return l
      })
    },
    // 右边选中
    rightCheckChange (check) {
      this.rightData = this.rightData.map(l => {
        if (l.key === check.key) {
          l.checked = !l.checked
        }
        return l
      })
    },
    // 数据向右穿梭
    toRight () {
      // 左减去,右加上
      const leftUnchecked = this.leftData.filter(l => !l.checked)
      const leftChecked = this.leftData.filter(l => l.checked)
      this.leftData = leftUnchecked
      this.rightData = [].concat(this.rightData, leftChecked).map(r => {
        r.checked = false
        return r
      })
      // 传递数据
      this.transferData()
    },
    // 数据向左穿梭
    toLeft () {
      // 右减去,左加上
      const rightUnchecked = this.rightData.filter(l => !l.checked)
      const rightChecked = this.rightData.filter(l => l.checked)
      this.rightData = rightUnchecked
      this.leftData = [].concat(this.leftData, rightChecked).map(r => {
        r.checked = false
        return r
      })
      // 传递数据
      this.transferData()
    }
  }
}
</script>
<style lang="less" scoped>
.custom-transfer-cls {
  display: flex;
  justify-content: space-between;
  min-height: 120px;
  .left-side,
  .right-side {
    height: 240px;
    overflow-y: scroll;
    background-color: white;
    width: 140px;
    border: 1px solid #eee;
    border-radius: 4px;
    /* 标题样式 */
    h4 {
      position: sticky;
      top: 0px;
      z-index: 9;
      background: white;
      text-align: center;
      font-weight: 400;
      margin-bottom: 16px;
    }
    /* 数据为空时的样式 */
    .empty-text {
      text-align: center;
      color: #ccc;
    }
    /* 列表每一项样式 */
    .item-cls {
      margin-left: 12px;
      margin-right: 12px;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
    /* 列表滚动条样式 */
    &::-webkit-scrollbar {
      width: 1px;
    }
    &::-webkit-scrollbar-thumb {
      background: #ccc;
    }
    &::-webkit-scrollbar-track {
      background: #ededed;
    }
  }
  /* 按钮样式 */
  .btn-cls {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .right-btn {
      margin-left: 0;
      margin-top: 8px;
    }
  }
}
</style>

小结

本文主要写了一个可拖动排序的穿梭框组件,更多关于拖动穿梭框CustormTransfer vue的资料请关注我们其它相关文章!

(0)

相关推荐

  • 基于Vue实现树形穿梭框的示例代码

    Vue 项目里面需要一个树形的穿梭框,但是 elementUI 和 ant-d 组件库的穿梭框组件效果都不是很好,因为源列表和目标列表都是需要树形结构的,所以说这个就很难搞,但是也不怕,因为大佬太多了,有插件可以提供给我们使用,接下来介绍一下这个插件. 树形穿梭框插件 el-tree-transfer 这个插件很好的实现了vue项目树形穿梭框的功能. 安装链接 上面的连接是npm插件地址,安装步骤也很简单. npm install el-tree-transfer --save 或者 npm i

  • VUE Elemen-ui之穿梭框使用方法详解

    本文实例为大家分享了VUE Elemen-ui之穿梭框使用方法,供大家参考,具体内容如下 背景: 现在需要使用穿梭框实现,角色的操作功能 需要使用 Element Transfer 穿梭框 HTML代码: <template> <el-card class="box-card" shadow="never" style="height: 700px;"> <div slot="header" cl

  • Vue 实现穿梭框功能的详细代码

    Vue - 实现穿梭框功能,效果图如下所示: css .transfer{ display: flex; justify-content: center; align-items: center; } .transfer>.list { width: 200px; height: 300px; border: 1px solid #000; list-style: none; } .content{ font-size: 30px; margin: 0 20px; } .list>li{ pa

  • vue3+typeScript穿梭框的实现示例

    前言 实现功能:模仿element穿梭框的简单功能 每周分享一个vue3+typeScript的小组件,我只想分享下自己的实现思路,楼主是个菜鸡前端,记录下实现过程,说不定对你有帮助. 效果展示 预览地址 github地址 开发过程 思路:用两个数组分别记录左右框框里面的值,根据复选框选中状态来实现删除增加即可 html部分 <div class="shuttle"> <!-- 左边列表 --> <div class="shuttle-box&q

  • Vue实现拖拽穿梭框功能四种方式实例详解

    目录 一.使用原生js实现拖拽 二.VUe使用js实现拖拽穿梭框 三.Vue 拖拽组件 vuedraggable 四.Awe-dnd指令封装 一.使用原生js实现拖拽 <html lang="en"> <head> <meta charset="UTF-8" /> <title>Lazyload</title> <style> .drag { background-color: skyblue;

  • vue穿梭框实现上下移动

    本文实例为大家分享了vue穿梭框实现上下移动的具体代码,供大家参考,具体内容如下 使用elementUI的树形组件 tree组件 功能需求: 1.左侧的子节点移动到右侧的表格中 2.右侧选中的内容移动到左侧树中,单一移动和全部移动 3.点击右侧节点实现上下移动 首先会遇到的问题可能是如何实现左侧只让子节点显示checkbox,我这边是根据后端返回了一个参数来判断是否是父节点(其实只要后端给父节点加一个nocheck=true就可以) // setLeftAgency :封装好的请求接口名称 se

  • 手写可拖动穿梭框组件CustormTransfer vue实现示例

    目录 本文内容 最终效果图 组件html布局 穿梭框左侧内容 穿梭框右侧内容 穿梭框中间向左.向右按钮 把排序好的穿梭数据传给父组件 整体代码 小结 本文内容 需求是实现类似 el-transfer的组件,右侧框内容可以拖动排序: 手写div样式 + vuedraggable组件实现. 最终效果图 组件html布局 新建一个组件文件 CustormTransfer.vue,穿梭框 html 分为左中右三部分,使用flex布局使其横向布局,此时代码如下 <template> <div cl

  • vue 通过下拉框组件学习vue中的父子通讯

    如果说vue组件化开发中第一步应该了解的是什么的话,那无疑是父子组件之间是如何实现通讯的(说白了就是父子组件中数据是如何传递的),只有理解了这一步,才能更好的开发组件 这里先提出两个关键词: props 与 emit : 写这个组件之前,先看看效果图: 组件开发分析: 既然是组件: 首先组件内部数据内容肯定是可变的(如上图中的"按时间排序"之类的),这必须由父组件传入(即父组件如何将数据传个父组件); 在选择了内容之后,如何将数据传出来(即子组件如何将数据传给父组件) 先写结构: 父组

  • 详解如何用VUE写一个多用模态框组件模版

    对于新手们来说,如何写一个可以多用的组件,还是有点难度的,组件如何重用,如何传值这些在实际使用中,是多少会存在一些障碍的,所以今天特意写一个最常用的模态框组件提供给大家,希望能帮助到您! 懒癌患者直接复制粘贴即可 Modal.vue组件 <template> <!-- 过渡动画 --> <transition name="modal-fade"> <!-- 关闭模态框事件 和 控制模态框是否显示 --> <div class=&qu

  • 手写Vue内置组件component的实现示例

    目录 前言 内置组件component的使用 component组件的原理分析 虚拟DOM与原生DOM render函数的使用 尝试手写实现component 总结 最近在复习Vue的源码,今天带大家手写实现一下Vue内置组件component,比较简单,最近面试有被问到. 前言 Vue大家都很熟悉,除了原生的组件,其自己也封装了一下内置组件,比如component,transition,keep-alive等等. component算是用的比较多的了,当我们遇到需要根据不同条件显示不同组件的时

  • 如何手写简易的 Vue Router

    前言 还是那样,懂得如何使用一个常用库,还得了解其原理或者怎么模拟实现,今天实现一下 vue-router . 有一些知识我这篇文章提到了,这里就不详细一步步写,请看我 手写一个简易的 Vuex 基本骨架 Vue 里面使用插件的方式是 Vue.use(plugin) ,这里贴出它的用法: 安装 Vue.js 插件.如果插件是一个对象,必须提供 install 方法.如果插件是一个函数,它会被作为 install 方法.install 方法调用时,会将 Vue 作为参数传入.这个方法的第一个参数是

  • 基于Vue3.0开发轻量级手机端弹框组件V3Popup的场景分析

    之前有分享一个vue2.x移动端弹框组件,今天给大家带来的是Vue3实现自定义弹框组件. V3Popup 基于vue3.x实现的移动端弹出框组件,集合msg.alert.dialog.modal.actionSheet.toast等多种效果.支持20+种自定义参数配置,旨在通过极简的布局.精简的调用方式解决多样化的弹框场景. v3popup 在开发之初参考借鉴了Vant3.ElementPlus等组件化思想.并且功能效果和之前vue2.0保持一致. ◆ 快速引入 在main.js中全局引入v3p

  • 微信小程序实现简单手写签名组件的方法实例

    目录 背景: 需求: 效果 一.思路 二.实现 1. 页面与样式 2. 初始化 3. 点击时 4. 签名时 三.总结 背景: 在做项目过程中,需要在微信小程序中实现手写签名组件.在网上找了微信小程序手写签名实现,但是都是不太理想.在实际运用中,会因为实时计算较多的贝塞尔曲线而产生卡顿.效果不理想.所以退一步,不需要笔锋以及笔迹模拟效果.只需要简单的手写签名实现. 需求: 可以实现用户在微信小程序上手写签名. 需要组件化. 效果 一.思路 在微信小程序中,我们使用canvas组件实现.将用户的输入

  • React手写一个手风琴组件示例

    目录 知识点 结构分析 AccordionItem子组件 Accordion容器组件 知识点 emotion语法 react语法 css语法 typescript类型语法 结构分析 根据上图,我们来分析一下,一个手风琴组件应该包含一个手风琴容器组件和多个手风琴子元素组件.因此,假设我们实现好了所有的逻辑,并写出使用demo,那么代码应该如下: <Accordion defaultIndex="1" onItemClick={console.log}> <Accordi

随机推荐