手把手教你系列- 卡片拖拽手势删除效果

背景介绍

手势效果是终端开发的常见需求,这篇文章手把手教你如何快速实现一个手势功能。最终效果first

项目初始化

新建一个项目 demo-gesture

  1. cml init project

进入项目

  1. cd demo-gesture

设计数据结构

我们要做一个列表,所以先设计一个数据结构如下:

  1. data = {
  2. list: [
  3. {
  4. // 用作样式名称
  5. className: 'inner-first',
  6. // 用作元素位置偏移
  7. transform: {
  8. y: 0,
  9. x: 0
  10. } ,
  11. }
  12. ]
  13. }

写进/src/pages/index/index.cml文件里面逻辑层JS代码里面

  1. <script>
  2. class Index {
  3. data = {
  4. list: [
  5. {
  6. className: 'inner-first',
  7. transform: {
  8. y: 0,
  9. x: 0
  10. }
  11. },
  12. {
  13. className: 'inner-second',
  14. transform: {
  15. y: 0,
  16. x: 0
  17. }
  18. },
  19. {
  20. className: 'inner-third',
  21. transform: {
  22. y: 0,
  23. x: 0
  24. }
  25. },
  26. {
  27. className: 'inner-fouth',
  28. transform: {
  29. y: 0,
  30. x: 0
  31. }
  32. }
  33. ],
  34. }
  35. methods = {
  36. }
  37. }
  38. export default new Index();
  39. </script>

书写CML使用数据

我们查看CML文档使用列表渲染

写进/src/pages/index/index.cml文件里面CML代码

  1. <template>
  2. <view class="wrapper">
  3. <view c-for="{{list}}" c-for-index="idx" c-for-item="itemName" data-index="{{idx}}" class="box {{itemName.className}}" style="transform: translate({{itemName.transform.x}}, 0);" >
  4. <text class="txt">{{idx}}</text>
  5. <view class="remove-btn" data-index="{{idx}}">
  6. <text class="rm"></text>
  7. </view>
  8. </view>
  9. </view>
  10. </template>

书写CMSS

查看CMSS文档,CMSS针对多端情况只能使用 flex 给页面布局,

  1. .wrapper{
  2. flex-direction: col;
  3. justify-content: space-around;
  4. height: 1200cpx;
  5. }
  6. .inner-first{
  7. background: red;
  8. }
  9. .inner-second{
  10. background: yellow;
  11. }
  12. .inner-third{
  13. background: blue;
  14. }
  15. .inner-fouth{
  16. background: green;
  17. }
  18. .box{
  19. height: 200cpx;
  20. }
  21. .txt{
  22. text-align: center;
  23. line-height: 50cpx;
  24. height: 50cpx;
  25. }
  26. .remove-btn{
  27. height: 50cpx;
  28. width: 50cpx;
  29. border: 1px solid hotpink;
  30. font-size: 10cpx;
  31. border-radius: 50cpx;
  32. transform: translate(780cpx, 0)
  33. }
  34. .rm{
  35. text-align: center;
  36. line-height: 50cpx;
  37. color: hotpink;
  38. }

目前三端效果展现如下

first

添加手势

我们查看事件绑定文档

给box元素添加事件绑定:

  1. c-bind:touchstart="eventhandler"
  2. c-bind:touchmove="eventhandler"
  3. c-bind:touchend="eventhandler"

添加事件回调函数

  1. methods = {
  2. eventHandler(event){
  3. // 获取当前手指位置
  4. let myPreX = event.changedTouches[0].pageX;
  5. // 获取当前手指所触摸的Item元素
  6. let index = event.currentTarget.dataset.index;
  7. // 获取当前手指所触摸的Item元素的偏移值
  8. let transform = this.list[index].transform;
  9. if(index >= this.list.length) {
  10. return;
  11. }
  12. console.log(index);
  13. // 手指触摸开始时将上一个X方向偏移设为null
  14. if(event.type == 'touchstart'){
  15. preX = null;
  16. }else if( event.type == 'touchmove'){
  17. if(preX !== null){
  18. let x = parseInt(transform.x) + myPreX - preX ;
  19. //x < 0 当手指往右滑动时不允许移动滑块
  20. // > 100 当手指往左滑动超过100时不允许滑动滑块
  21. if(Math.abs(x) < 100 && x < 0){
  22. //数据驱动视图更改
  23. transform.x = x + 'cpx';
  24. }
  25. }
  26. // 保存最后的位置
  27. preX = myPreX;
  28. }else if(event.type == 'touchend'){
  29. if(Math.abs(parseInt(transform.x)) < 90){
  30. transform.x = 0;
  31. }
  32. }
  33. }
  34. }

添加删除按钮事件回调函数

给remove-btn元素添加事件绑定:

  1. c-bind:click="onRemove"
  1. methods = {
  2. onRemove(e){
  3. let index = e.currentTarget.dataset.index;
  4. this.list.splice(index, 1);
  5. }
  6. }

/src/pages/index/index.cml 最终全部代码如下:

  1. <template>
  2. <view class="wrapper">
  3. <view c-for="{{list}}" c-for-index="idx" c-for-item="itemName" data-index="{{idx}}" class="box {{itemName.className}}" style="transform: translate({{itemName.transform.x}}, 0);"
  4. c-bind:touchstart="eventHandler"
  5. c-bind:touchmove="eventHandler"
  6. c-bind:touchend="eventHandler"
  7. >
  8. <text class="txt">{{idx}}</text>
  9. <view class="remove-btn" c-bind:click="onRemove" data-index="{{idx}}" >
  10. <text class="rm" ></text>
  11. </view>
  12. </view>
  13. </view>
  14. </template>
  15. <script>
  16. let preX = null;
  17. class Index {
  18. data = {
  19. list: [
  20. {
  21. className: 'inner-first',
  22. transform: {
  23. y: 0,
  24. x: 0
  25. }
  26. },
  27. {
  28. className: 'inner-second',
  29. transform: {
  30. y: 0,
  31. x: 0
  32. }
  33. },
  34. {
  35. className: 'inner-third',
  36. transform: {
  37. y: 0,
  38. x: 0
  39. }
  40. },
  41. {
  42. className: 'inner-fouth',
  43. transform: {
  44. y: 0,
  45. x: 0
  46. }
  47. }
  48. ],
  49. }
  50. methods = {
  51. eventHandler(event){
  52. // 获取当前手指位置
  53. let myPreX = event.changedTouches[0].pageX;
  54. // 获取当前手指所触摸的Item元素
  55. let index = event.currentTarget.dataset.index;
  56. // 获取当前手指所触摸的Item元素的偏移值
  57. if(index >= this.list.length) {
  58. return;
  59. }
  60. console.log(index);
  61. let transform = this.list[index].transform;
  62. // 手指触摸开始时将上一个X方向偏移设为null
  63. if(event.type == 'touchstart'){
  64. preX = null;
  65. }else if( event.type == 'touchmove'){
  66. if(preX !== null){
  67. let x = parseInt(transform.x) + myPreX - preX ;
  68. //x < 0 当手指往右滑动时不允许移动滑块
  69. // > 100 当手指往左滑动超过100时不允许滑动滑块
  70. if(Math.abs(x) < 100 && x < 0){
  71. //数据驱动视图更改
  72. transform.x = x + 'cpx';
  73. }
  74. }
  75. // 保存最后的位置
  76. preX = myPreX;
  77. }else if(event.type == 'touchend'){
  78. if(Math.abs(parseInt(transform.x)) < 90){
  79. transform.x = 0;
  80. }
  81. }
  82. },
  83. onRemove(e){
  84. let index = e.currentTarget.dataset.index;
  85. this.list.splice(index, 1);
  86. }
  87. }
  88. }
  89. export default new Index();
  90. </script>
  91. <style scoped>
  92. .wrapper{
  93. flex-direction: col;
  94. justify-content: space-around;
  95. height: 1200cpx;
  96. }
  97. .inner-first{
  98. background: red;
  99. }
  100. .inner-second{
  101. background: yellow;
  102. }
  103. .inner-third{
  104. background: blue;
  105. }
  106. .inner-fouth{
  107. background: green;
  108. }
  109. .box{
  110. height: 200cpx;
  111. }
  112. .txt{
  113. text-align: center;
  114. line-height: 50cpx;
  115. height: 50cpx;
  116. }
  117. .remove-btn{
  118. height: 50cpx;
  119. width: 50cpx;
  120. border: 1px solid hotpink;
  121. font-size: 10cpx;
  122. border-radius: 50cpx;
  123. transform: translate(780cpx, 0)
  124. }
  125. .rm{
  126. text-align: center;
  127. line-height: 50cpx;
  128. color: hotpink;
  129. }
  130. </style>
  131. <script cml-type="json">
  132. {
  133. }
  134. </script>

最终不到150行代码实现三端拖拽效果:first