Taro UI框架开发小程序实现左滑喜欢右滑不喜欢效果的示例代码


Posted in Javascript onMay 18, 2020

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

摘要:

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

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

先上效果图:

Taro UI框架开发小程序实现左滑喜欢右滑不喜欢效果的示例代码

主要技术: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开发小程序实现左滑喜欢右滑不喜欢效果的文章就介绍到这了,更多相关小程序实现左滑喜欢右滑不喜欢内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js中将URL中的参数提取出来作为对象的实现代码
Aug 16 Javascript
利用div+jquery自定义滚动条样式的2种方法
Jul 18 Javascript
javascript jq 弹出层实例
Aug 25 Javascript
jQuery事件绑定on()、bind()与delegate() 方法详解
Jun 03 Javascript
JavaScript中的Math.sin()方法使用详解
Jun 15 Javascript
JavaScript中利用Array和Object实现Map的方法
Jul 27 Javascript
基于javascript实现九宫格大转盘效果
May 28 Javascript
微信小程序 时间格式化(util.formatTime(new Date))详解
Nov 16 Javascript
详解VUE的状态控制与延时加载刷新
Mar 27 Javascript
浅谈关于.vue文件中style的scoped属性
Aug 19 Javascript
使用mint-ui实现省市区三级联动效果的示例代码
Feb 09 Javascript
node 使用 async 控制并发的方法
May 07 Javascript
vue el-tree 默认展开第一个节点的实现代码
May 15 #Javascript
基于leaflet.js实现修改地图主题样式的流程分析
May 15 #Javascript
uni-app从安装到卸载的入门教程
May 15 #Javascript
Vue数据双向绑定原理实例解析
May 15 #Javascript
JavaScript鼠标悬停事件用法解析
May 15 #Javascript
JavaScript enum枚举类型定义及使用方法
May 15 #Javascript
Vue如何基于es6导入外部js文件
May 15 #Javascript
You might like
正义联盟的终局之战《天启星战争》将成为DC动画宇宙的最后一部
2020/04/09 欧美动漫
编写自己的php扩展函数
2006/10/09 PHP
php遍历目录输出目录及其下的所有文件示例
2014/01/27 PHP
php生成PDF格式文件并且加密
2015/06/22 PHP
Yii中表单用法实例详解
2016/01/05 PHP
必须收藏的23个php实用代码片段
2016/02/02 PHP
老生常谈ThinkPHP中的行为扩展和插件(推荐)
2017/05/05 PHP
经典海量jQuery插件 大家可以收藏一下
2010/02/07 Javascript
JS HTML5 音乐天气播放器(Ajax获取天气信息)
2013/05/26 Javascript
在ASP.NET中使用JavaScript脚本的方法
2013/11/12 Javascript
javascript使用数组的push方法完成快速排序
2014/09/15 Javascript
javascript+html5实现仿flash滚动播放图片的方法
2015/04/27 Javascript
Javascript中setTimeOut和setInterval的定时器用法
2015/06/12 Javascript
jQuery实现输入框下拉列表树插件特效代码分享
2015/08/27 Javascript
jQuery实现调整表格单列顺序完整实例
2016/06/20 Javascript
JS回调函数基本定义与用法实例分析
2017/05/24 Javascript
使用Angular CLI进行Build(构建)和Serve详解
2018/03/24 Javascript
JS实现动态生成html table表格的方法分析
2018/07/11 Javascript
用element的upload组件实现多图片上传和压缩的示例代码
2019/02/12 Javascript
详解JavaScript 作用域
2020/07/14 Javascript
js实现幻灯片轮播图
2020/08/14 Javascript
微信小程序实现翻牌抽奖动画
2020/09/21 Javascript
vue下拉刷新组件的开发及slot的使用详解
2020/12/23 Vue.js
使用Python的package机制如何简化utils包设计详解
2017/12/11 Python
python并发编程之线程实例解析
2017/12/27 Python
Python如何使用Gitlab API实现批量的合并分支
2019/11/27 Python
Python3.7下安装pyqt5的方法步骤(图文)
2020/05/12 Python
Python通过zookeeper实现分布式服务代码解析
2020/07/22 Python
Soft Cotton捷克:来自爱琴海棉花的浴袍
2017/02/01 全球购物
LightInTheBox法国站:中国跨境电商
2020/03/05 全球购物
九一八事变演讲稿范文
2014/09/14 职场文书
大学生翘课检讨书范文
2014/10/06 职场文书
讲文明倡议书
2015/04/29 职场文书
PHP控制循环操作的时间
2021/04/01 PHP
「睡美人」爱洛公主粘土人开订
2022/03/22 日漫
Python+SeaTable实现计算两个日期间的工作日天数
2022/07/07 Python