Taro UI框架开发小程序实现左滑喜欢右滑不喜欢效果

 更新时间:2020年5月16日 08:39  点击:1382

Taro 就是可以用 React 语法写小程序的框架,拥有多端转换能力,一套代码可编译为微信小程序、百度小程序、支付宝小程序、H5、RN等

摘要:

年后入职了一家新公司,与前同事交接完之后,发现公司有一个四端的项目(iOS,Android,H5,小程序),iOS和安卓都实现了左滑右滑的效果,而h5和小程序端没实现,询问得知前同事因网上没找到对应的插件,相关博客也特别少,所以没做就搁置下来了。

趁这段时间相对来说比较富裕,于是乎在网上也搜索了一下,发现确实很少,但是有人提到可以用小程序可拖动组件movable-view来实现,自己尝试来一下发现可行,于是来写这篇博客记录一下,希望能帮助到后面需要用到这个功能的人!

先上效果图:

主要技术:Taro+Taro UI+React(如果你是小程序原生或者uniapp+vue写法都差不多,可以通用)

可拖动组件文档地址:

Taro:https://taro-docs.jd.com/taro/docs/components/viewContainer/movable-view.html

微信小程序:https://developers.weixin.qq.com/miniprogram/dev/component/movable-view.html

思路:

一,我们首先把movable-area和movable-view标签写出来;

<movable-area>
 <movable-view>
  ......
 </movable-view>
</movable-area>

二,我们可以看到文档里面有一个onChange方法,即拖动过程中触发的事件;

<movable-area>
 <movable-view onChange ={this. onChange.bind(this)}>
  ......
 </movable-view>
</movable-area>

// 触发方法,打印参数
onChange(e) {
 console.log('参数',e);
}

我们可以看到打印出了,拖动的位置和产生移动的原因等;

三,我们接着加入开始onTouchstart,移动onTouchmove,结束onTouchcancel,onTouchend三个事件方法;

<MovableView 
 key={item.id} 
 onTouchcancel={this.onCancel} 
 onTouchend={this.onCancel} 
 onTouchstart={this.onTouchStart} 
 onTouchmove={this.onTouchMove} 
 x={this.state.x} // 横坐标位置
 y={this.state.y} // 纵坐标位置
 direction='all' // 移动方向都可以
 outOfBounds // 可超过可移动区域
 className='shop-imgbox' 
> 
<--中间加入图片之类的滑动内容-->
</MovableView>

初始数据如下:

state = { 
 x: '16', 
 y: '16', 
 like: false, 
 unlike: false, 
 shopList: [ 
  { 
  img: 'https://edgefix-image.edgecom.top/ABD846F6672997A7F76CD38E8A57F954.jpg', 
  }, 
  { 
  img: 'https://edgefix-image.edgecom.top/F6E5801C304CC76DA63C02C9FB38B8F4.jpg', 
  }, 
  { 
  img: 'https://edgefix-image.edgecom.top/D518952AD1DD61B2D32556E20CC527C4.jpg', 
  }, 
  { 
  img: 'https://edgefix-image.edgecom.top/1D187E28B349679908A44BBE81F3D3CA.jpg', 
  }, 
  { 
  img: 'https://edgefix-image.edgecom.top/1129A411AC9CF5F81187CBED181B6F57.jpg', 
  } 
 ] 
 }

三个方法我们可以取到移动后改变的位置,来改变喜欢与不喜欢的状态css,以及实现卡片滑动的效果:

1.触摸触发的时候,我们获取到刚刚开始触摸卡片的x,y的位置坐标;

2.在触摸滑动时,我们通过滑动后的位置-滑动前的位置,来判断距离多少来改变喜欢和不喜欢的值;

3.当手离开时,触发取消事件,我们需要把状态数据改为原始值,即回到最初的状态;

// 触摸触发 
 onTouchStart(e) { 
 console.log('222',e.touches[0].pageX); 
 this.setState({ 
  x: e.touches[0].pageX, 
  y: e.touches[0].pageY, 
 }); 
 } 
 // 触摸移动 
 onTouchMove(e) { 
 console.log('333',e.touches[0].pageX); 
 let dx = e.touches[0].pageX - this.state.x; 
 if (dx > 50) { 
  this.setState({ 
  like: true, 
  unlike: false, 
  }); 
 } else if (dx < -50) { 
  this.setState({ 
  like: false, 
  unlike: true, 
  }); 
 } else { 
  this.setState({ 
  like: false, 
  unlike: false, 
  }); 
 } 
 } 
 // 取消 
 onCancel(e) { 
 console.log('444',e.changedTouches[0].pageX); 
 this.setState({ 
  x: '16', 
  y: '16', 
  like: false, 
  unlike: false, 
 }); 
 }

当我们写到这里,我们去拖动我们的卡片时,你会发现确实可以拖动,并且取消的时候会回到原点,但是同样你也会发现一个问题,就是你拖动的时候,五张卡片都被触发来移动的效果,出现了触点混乱的问题,查找问题发现卡片共用了x,y,因此我们可以给每张卡片设置独立的参数;

四,给每张卡片独立的参数并且设置卡片倾斜度效果;

1.设置倾斜度效果

style={{transform:'rotate('+this.state.tiltAngle[index]+'deg)'}}

然后我们通过卡片移动位置计算出一个你决定合适的倾斜角度;

// 拖动后相差距离进行换算角度
let dxangle = (e.touches[0].pageX - this.state.startX) * 45 / 500;

2.设置独立的参数

方法携带索引,我们取到对应的卡片index,来改变对应卡片的数据;

<MovableView 
 key={item.id}
 onTouchcancel={this.onCancel.bind(this,index)}
 onTouchend={this.onCancel.bind(this,index)}
 onTouchstart={this.onTouchStart.bind(this,index)}
 onTouchmove={this.onTouchMove.bind(this,index)}
 x={this.state.x[index]} 
 y={this.state.y[index]} 
 direction='all' 
 outOfBounds 
 className='shop-imgbox'
>
</MovableView>

同时,我们需要改变初始参数的形式为数组,我们通过索引改变对应卡片的值;

state = {
 // 开始位置
 startX: '',
 // 开始位置-最终位置距离
 placeX: '',
 // 倾斜角度
 tiltAngle: ['0','0','0','0','0'],
 // 坐标
 x: ['16','16','16','16','16'],
 y: ['16','16','16','16','16'],
 // 是否喜欢状态
 like: [false,false,false,false,false],
 unlike: [false,false,false,false,false],
 // 推荐商品数组
 shopList: [
  {
  id: 1,
  img: 'https://edgefix-image.edgecom.top/ABD846F6672997A7F76CD38E8A57F954.jpg',
  },
  {
  id: 2,
  img: 'https://edgefix-image.edgecom.top/F6E5801C304CC76DA63C02C9FB38B8F4.jpg',
  },
  {
  id: 3,
  img: 'https://edgefix-image.edgecom.top/D518952AD1DD61B2D32556E20CC527C4.jpg',
  },
  {
  id: 4,
  img: 'https://edgefix-image.edgecom.top/1D187E28B349679908A44BBE81F3D3CA.jpg',
  },
  {
  id: 5,
  img: 'https://edgefix-image.edgecom.top/1129A411AC9CF5F81187CBED181B6F57.jpg',
  }
 ]
 }

方法我们就举一个例子,比如onTouchStart方法,我们遍历卡片数组,通过判断索引来得到是那张卡片,从而来改变对应值

// 触摸触发
 onTouchStart(index,e) {
 console.log('1111',index,e.touches[0].pageX,e.touches[0].pageY);
 // 重定义数组
 var againX = [];
 var againY = [];
 // 遍历,判断拖动的该数组的位置
 for (var i=0; i<this.state.shopList.length; i++){
  if (i == index) {
  againX[i] = e.touches[0].pageX;
  againY[i] = e.touches[0].pageY;
  } else {
  againX[i] = '16';
  againY[i] = '16';
  }
 }
 // 赋值
 this.setState({
  startX: e.touches[0].pageX,
  x: againX,
  y: againY,
 });
 }

这样,我们运行代码,发现拖动第一张卡片不会影响到后面卡片的位置了,

同时,我们现在拖动卡片删除的是数组,在实际项目中,我们在触发删除数组的地方接入接口,调用喜欢,不喜欢改变数据参数,从而也能改变数组的长度;

五,完整代码;

下面我将贴出完整的代码供大家参考

html文件:

import Taro, { Component } from '@tarojs/taro';
import { View, Image, Button, Text, MovableArea, MovableView } from '@tarojs/components';
import { observer, inject } from '@tarojs/mobx';
import { AtButton, AtFloatLayout } from 'taro-ui';
import userStore from '../../store/user.store';
import './stroll.scss';
@inject('userStore')
@observer
class Stroll extends Component {
 config = {
 navigationBarTitleText: '逛',
 }
 state = {
 // 开始位置
 startX: '',
 // 开始位置-最终位置距离
 placeX: '',
 // 倾斜角度
 tiltAngle: ['0','0','0','0','0'],
 // 坐标
 x: ['16','16','16','16','16'],
 y: ['16','16','16','16','16'],
 // 是否喜欢状态
 like: [false,false,false,false,false],
 unlike: [false,false,false,false,false],
 // 推荐商品数组
 shopList: [
  {
  id: 1,
  img: 'https://edgefix-image.edgecom.top/ABD846F6672997A7F76CD38E8A57F954.jpg',
  },
  {
  id: 2,
  img: 'https://edgefix-image.edgecom.top/F6E5801C304CC76DA63C02C9FB38B8F4.jpg',
  },
  {
  id: 3,
  img: 'https://edgefix-image.edgecom.top/D518952AD1DD61B2D32556E20CC527C4.jpg',
  },
  {
  id: 4,
  img: 'https://edgefix-image.edgecom.top/1D187E28B349679908A44BBE81F3D3CA.jpg',
  },
  {
  id: 5,
  img: 'https://edgefix-image.edgecom.top/1129A411AC9CF5F81187CBED181B6F57.jpg',
  }
 ]
 }
 componentWillMount () { }
 componentWillReact () { }
 componentDidMount () {
 }
 // 触摸触发
 onTouchStart(index,e) {
 console.log('1111',index,e.touches[0].pageX,e.touches[0].pageY);
 // 重定义数组
 var againX = [];
 var againY = [];
 // 遍历,判断拖动的该数组的位置
 for (var i=0; i<this.state.shopList.length; i++){
  if (i == index) {
  againX[i] = e.touches[0].pageX;
  againY[i] = e.touches[0].pageY;
  } else {
  againX[i] = '16';
  againY[i] = '16';
  }
 }
 // 赋值
 this.setState({
  startX: e.touches[0].pageX,
  x: againX,
  y: againY,
 });
 }
 // 触摸离开
 onTouchMove(index,e) {
 console.log('2222',index,e.touches[0].pageX,e.touches[0].pageY);
 // 重定义数组
 var tiltAngleT = [];
 var againX = [];
 var againY = [];
 // 拖动后相差距离
 let dxplace = e.touches[0].pageX - this.state.startX;
 // 拖动后相差距离进行换算角度
 let dxangle = (e.touches[0].pageX - this.state.startX) * 45 / 500;
 console.log(dxangle);
 // 遍历,判断拖动的该数组的位置
 for (var i=0; i<this.state.shopList.length; i++){
  if (i == index && dxplace > 50) {
  tiltAngleT[i] = dxangle,
  againX[i] = true;
  againY[i] = false;
  } else if (i == index && dxplace <= -50) {
  tiltAngleT[i] = dxangle,
  againX[i] = false;
  againY[i] = true;
  } else if (i == index && dxplace < 50 && dxplace > -50) {
  tiltAngleT[i] = dxangle,
  againX[i] = false;
  againY[i] = false;
  } else {
  tiltAngleT[i] = '0',
  againX[i] = false;
  againY[i] = false;
  }
 }
 // 赋值
 this.setState({
  placeX: dxplace,
  tiltAngle: tiltAngleT,
  like: againX,
  unlike: againY,
 });
 }
 // 取消
 onCancel(index,e) {
 console.log('3333',index,e.changedTouches[0].pageX,e.changedTouches[0].pageY);
 // 赋值
 this.setState({
  tiltAngle: ['0','0','0','0','0'],
  x: ['16','16','16','16','16'],
  y: ['16','16','16','16','16'],
  like: [false,false,false,false,false],
  unlike: [false,false,false,false,false],
 });
 // 如果偏移已经达到则清除第一张图片
 if (this.state.placeX > 50 || this.state.placeX < -50) {
  this.setState({
  shopList: this.state.shopList.splice(1,4),
  });
 }
 }
 // 不喜欢按钮点击
 dislikebtn() {
 // 改变按钮的状态以及图片位置及显示
 this.setState({
  tiltAngle: ['-18','0','0','0','0'],
  x: ['-30','16','16','16','16'],
  y: ['267','16','16','16','16'],
  unlike: [true,false,false,false,false],
 }, () => {
  setTimeout( () => {
  this.setState({
   tiltAngle: ['0','0','0','0','0'],
   x: ['16','16','16','16','16'],
   y: ['16','16','16','16','16'],
   unlike: [false,false,false,false,false],
   shopList: this.state.shopList.splice(1,4),
  });
  },100);
 });
 }
 // 喜欢按钮点击
 likebtn() {
 // 改变按钮的状态以及图片位置及显示
 this.setState({
  tiltAngle: ['18','0','0','0','0'],
  x: ['284','16','16','16','16'],
  y: ['267','16','16','16','16'],
  like: [true,false,false,false,false],
 }, () => {
  setTimeout( () => {
  this.setState({
   tiltAngle: ['0','0','0','0','0'],
   x: ['16','16','16','16','16'],
   y: ['16','16','16','16','16'],
   like: [false,false,false,false,false],
   shopList: this.state.shopList.splice(1,4),
  });
  },100);
 });
 }
 componentWillUnmount () { }
 componentDidShow () {
 }
 componentDidHide () { }
 render() {
 return (
  <View className='stroll-tab'>
  <View className='stroll-text'>
   <Text className='text-tip1'>搭配师每天为你推荐5件单品</Text>
   <View className='text-tip2'>
   <Text className='t1'>右滑喜欢</Text>
   <Image src={require('./img/ic_like.png')} className='icon-image'></Image>
   <Text className='t1'>,左滑不喜欢</Text>
   <Image src={require('./img/ic_dislike.png')} className='icon-image'></Image>
   </View>
  </View>
  {
   this.state.shopList.length != 0&&
   <MovableArea className='stroll-shop'>
   {
   this.state.shopList&&this.state.shopList.map((item,index) => {
    return(
    <MovableView 
     key={item.id}
     onTouchcancel={this.onCancel.bind(this,index)}
     onTouchend={this.onCancel.bind(this,index)}
     onTouchstart={this.onTouchStart.bind(this,index)}
     onTouchmove={this.onTouchMove.bind(this,index)}
     x={this.state.x[index]} 
     y={this.state.y[index]} 
     direction='all' 
     outOfBounds 
     className='shop-imgbox'
    >
     <View className='images-box' style={{transform:'rotate('+this.state.tiltAngle[index]+'deg)'}}>
     <Image src={item.img} className='images'></Image>
     {
      this.state.like[index]==true&&
      <Image src={require('./img/text_like.png')} className='imagelike'></Image>
     }
     {
      this.state.unlike[index]==true&&
      <Image src={require('./img/text_dislike.png')} className='imageunlike'></Image>
     }
     </View>
    </MovableView>
   );})
   }
   </MovableArea>
  }
  {
   this.state.shopList.length === 0&&
   <View className='noshop-card'>
   <Image src={require('./img/noshop.png')} className='noshop-image'></Image>
   </View>
  }
  <View className='stroll-fotter'>
   {
   this.state.shopList.length != 0&&
   <View className='fot-twoimg'>
    {
    this.state.unlike[0]==false&&
    <Image src={require('./img/dislike_default.png')} className='dislike-image' onClick={this.dislikebtn.bind(this)}></Image>
    }
    {
    this.state.unlike[0]==true&&
    <Image src={require('./img/dislike_click.png')} className='dislike-image'></Image>
    }
    {
    this.state.like[0]==false&&
    <Image src={require('./img/like_default.png')} className='like-image' onClick={this.likebtn.bind(this)}></Image>
    }
    {
    this.state.like[0]==true&&
    <Image src={require('./img/like_click.png')} className='like-image'></Image>
    }
   </View>
   }
   <Text className='fot-text'>查看我喜欢的</Text>
  </View>
  </View>
 );
 }
}
export default Stroll;

css文件:

page {
 height: 100%;
 background: #F6F6F6;
}
.stroll-tab {
 width: 100%;
 min-height: 100vh;
 background: #F6F6F6;
 .stroll-text {
 width: 100%;
 margin-top: 40px;
 display: flex;
 flex-direction: column;
 align-items: center;
 .text-tip1 {
  font-size: 28px;
  color: #333333;
 }
 .text-tip2 {
  display: flex;
  flex-direction: row;
  align-items: center;
  .t1 {
  font-size: 28px;
  color: #333333;
  }
  .icon-image {
  width:20px;
  height:20px;
  }
 }
 }
 .stroll-shop {
 width: 100%;
 height: 700px;
 margin-top: 40px;
 .shop-imgbox {
  height: 600px;
  border-radius: 24px;
  .images-box {
  width: 100%;
  height: 520px;
  border-radius: 24px;
  box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.1);
  background-color: #fff;
  position: relative;
  .images {
   width: 606px;
   height: 480px;
   position: absolute;
   left: 40px;
   top: 20px;
  }
  .imagelike {
   width: 96px;
   height: 48px;
   position: absolute;
   right: 40px;
   top: 20px;
  }
  .imageunlike {
   width: 148px;
   height: 48px;
   position: absolute;
   left: 40px;
   top: 20px;
  }
  }
 }
 .shop-imgbox:nth-child(1) {
  width: 686px;
  z-index: 50;
 }
 .shop-imgbox:nth-child(2) {
  width: 676px;
  z-index: 40;
  margin: 15px 0px 0px 5px;
 }
 .shop-imgbox:nth-child(3) {
  width: 666px;
  z-index: 30;
  margin: 30px 0px 0px 10px;
 }
 .shop-imgbox:nth-child(4) {
  width: 656px;
  z-index: 20;
  margin: 0px 0px 0px 15px;
 }
 .shop-imgbox:nth-child(5) {
  width: 646px;
  z-index: 10;
  margin: 0px 0px 0px 20px;
 }
 }
 .noshop-card {
 width: 100%;
 margin-top: 40px;
 padding: 0px 16px;
 .noshop-image {
  width: 100%;
  height: 806px;
 }
 }
 .stroll-fotter {
 width: 100%;
 display: flex;
 flex-direction: column;
 align-items: center;
 margin-top: 20px;
 .fot-twoimg {
  display: flex;
  flex-direction: row;
  align-items: center;
  .dislike-image {
  width: 120px;
  height: 120px;
  }
  .like-image {
  width: 120px;
  height: 120px;
  margin-left: 48px;
  }
 }
 .fot-text {
  color: #368BE5;
  font-size: 28px;
  margin-top: 40px;
  margin-bottom: 50px;
 }
 }
}

总结

到此这篇关于Taro UI开发小程序实现左滑喜欢右滑不喜欢效果的文章就介绍到这了,更多相关小程序实现左滑喜欢右滑不喜欢内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • 微信小程序 页面传值详解

    这篇文章主要介绍了微信小程序 页面传值详解的相关资料,需要的朋友可以参考下...2017-03-13
  • uniapp微信小程序:key失效的解决方法

    这篇文章主要介绍了uniapp微信小程序:key失效的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-20
  • c#异步读取数据库与异步更新ui的代码实现

    这篇文章主要介绍了c#从数据库里取得数据并异步更新ui的方法,大家参考使用吧...2020-06-25
  • No module named ‘win32gui‘ 的解决方法(踩坑之旅)

    这篇文章主要介绍了No module named ‘win32gui‘ 的解决方法(踩坑之旅),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-18
  • PHP常用的小程序代码段

    本文实例讲述了PHP常用的小程序代码段。分享给大家供大家参考,具体如下:1.计算两个时间的相差几天$startdate=strtotime("2009-12-09");$enddate=strtotime("2009-12-05");上面的php时间日期函数strtotime已经把字符串...2015-11-24
  • 解决vant-UI库修改样式无效的问题

    这篇文章主要介绍了解决vant-UI库修改样式无效的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-03
  • vue+element ui实现锚点定位

    这篇文章主要为大家详细介绍了vue+element ui实现锚点定位,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-06-29
  • 微信小程序自定义tabbar组件

    这篇文章主要为大家详细介绍了微信小程序自定义tabbar组件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-03-14
  • 微信小程序 网络请求(GET请求)详解

    这篇文章主要介绍了微信小程序 网络请求(GET请求)详解的相关资料,需要的朋友可以参考下...2016-11-22
  • 微信小程序如何获取图片宽度与高度

    这篇文章主要给大家介绍了关于微信小程序如何获取图片宽度与高度的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-10
  • 微信小程序二维码生成工具 weapp-qrcode详解

    这篇文章主要介绍了微信小程序 二维码生成工具 weapp-qrcode详解,教大家如何在项目中引入weapp-qrcode.js文件,通过实例代码给大家介绍的非常详细,需要的朋友可以参考下...2021-10-23
  • Python爬取微信小程序通用方法代码实例详解

    这篇文章主要介绍了Python爬取微信小程序通用方法代码实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-29
  • 微信小程序手势操作之单触摸点与多触摸点

    这篇文章主要介绍了微信小程序手势操作之单触摸点与多触摸点的相关资料,需要的朋友可以参考下...2017-03-13
  • js如何构造elementUI树状菜单的数据结构详解

    由于业务需要,要求实现树形菜单,且菜单数据由后台返回,下面这篇文章主要给大家介绍了关于js如何构造elementUI树状菜单的数据结构的相关资料,需要的朋友可以参考下...2021-05-13
  • 微信小程序(应用号)开发新闻客户端实例

    这篇文章主要介绍了微信小程序(应用号)开发新闻客户端实例的相关资料,需要的朋友可以参考下...2016-10-25
  • 手把手教你uniapp和小程序分包(图文)

    本文主要介绍了手把手教你uniapp和小程序分包,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-02
  • easyUI下拉列表点击事件使用方法

    这篇文章主要为大家详细介绍了easyUI下拉列表点击事件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-05-22
  • 微信小程序 页面跳转传递值几种方法详解

    这篇文章主要介绍了微信小程序 页面跳转传递值几种方法详解的相关资料,需要的朋友可以参考下...2017-01-16
  • 微信小程序实现canvas分享朋友圈海报

    这篇文章主要为大家详细介绍了微信小程序实现canvas分享朋友圈海报,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-21
  • 详解element-ui 表单校验 Rules 配置 常用黑科技

    这篇文章主要介绍了element-ui 表单校验 Rules 配置 常用黑科技,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-07-11