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 相关文章推荐
javascript 对象的定义方法
Jan 10 Javascript
圣诞节Merry Christmas给博客添加浪漫的下雪效果基于jquery实现
Dec 27 Javascript
jquery select动态加载选择(兼容各种浏览器)
Feb 01 Javascript
解决jQuery动态获取手机屏幕高和宽的问题
May 07 Javascript
javascript在IE下trim函数无法使用的解决方法
Sep 12 Javascript
简述Jquery与DOM对象
Jul 10 Javascript
纯JavaScript实现的分页插件实例
Jul 14 Javascript
javascript瀑布流式图片懒加载实例解析与优化
Feb 23 Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(同步显示图像位置信息)
Dec 02 Javascript
js Element Traversal规范中的元素遍历方法
Apr 19 Javascript
windows实现npm和cnpm安装步骤
Oct 24 Javascript
vue项目接口管理,所有接口都在apis文件夹中统一管理操作
Aug 13 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
php切割页面div内容的实现代码分享
2012/07/31 PHP
php短网址和数字之间相互转换的方法
2015/03/13 PHP
PHP快速排序quicksort实例详解
2016/09/28 PHP
php 函数使用可变数量的参数方法
2017/05/02 PHP
动态加载dtree.js树treeview(示例代码)
2013/12/17 Javascript
jQuery实现带有洗牌效果的动画分页实例
2015/08/31 Javascript
JavaScript原生xmlHttp与jquery的ajax方法json数据格式实例
2015/12/04 Javascript
如何解决IONIC页面底部被遮住无法向上滚动问题
2016/09/06 Javascript
jQuery中delegate()方法的用法详解
2016/10/13 Javascript
基于jQuery实现瀑布流页面
2017/04/11 jQuery
nodejs multer实现文件上传与下载
2017/05/10 NodeJs
JS中定位 position 的使用实例代码
2017/08/06 Javascript
Node.js调用fs.renameSync报错(Error: EXDEV, cross-device link not permitted)
2017/12/27 Javascript
JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例
2018/07/27 Javascript
微信小程序页面间跳转传参方式总结
2019/06/13 Javascript
PHP读取远程txt文档到数组并实现遍历
2020/08/25 Javascript
[01:04:01]2014 DOTA2华西杯精英邀请赛5 24 DK VS VG
2014/05/25 DOTA
二种python发送邮件实例讲解(python发邮件附件可以使用email模块实现)
2013/12/03 Python
pymongo实现控制mongodb中数字字段做加法的方法
2015/03/26 Python
python读取二进制mnist实例详解
2017/05/31 Python
pandas带有重复索引操作方法
2018/06/08 Python
Selenium定时刷新网页的实现代码
2018/10/31 Python
tensorflow dataset.shuffle、dataset.batch、dataset.repeat顺序区别详解
2020/06/03 Python
Python如何测试stdout输出
2020/08/10 Python
css背景图片的背景裁切、背景透明度、背景变换等效果运用
2012/12/24 HTML / CSS
详解HTML5中的元素与元素
2015/08/17 HTML / CSS
HTML5中的Web Notification桌面右下角通知功能的实现
2018/04/19 HTML / CSS
天猫超市:阿里巴巴打造的网上超市
2016/11/02 全球购物
Mio Skincare法国官网:身体紧致及孕期身体护理
2018/04/04 全球购物
西部世纪面试题
2014/12/05 面试题
先进事迹报告会感言
2014/01/24 职场文书
大学生个人求职口试自我评价
2014/02/16 职场文书
党员创先争优承诺书
2014/03/26 职场文书
艺术学院毕业生求职信
2014/07/09 职场文书
2015年材料员工作总结
2015/04/30 职场文书
解决IDEA翻译插件Translation报错更新TTK失败不能使用
2022/04/24 Python