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 页面内容搜索,类似于 Ctrl+F功能的实现代码
Aug 13 Javascript
javascript dom 操作详解 js加强
Jul 13 Javascript
javascript 面向对象封装与继承
Nov 27 Javascript
5个数组Array方法: indexOf、filter、forEach、map、reduce使用实例
Jan 29 Javascript
纯JS实现轮播图
Feb 22 Javascript
利用angularjs1.4制作的简易滑动门效果
Feb 28 Javascript
小程序实现多选框功能
Oct 30 Javascript
详解vue在项目中使用百度地图
Mar 26 Javascript
在node环境下parse Smarty模板的使用示例代码
Nov 15 Javascript
javascript 函数的暂停和恢复实例详解
Apr 25 Javascript
EXTJS7实现点击拖拉选择文本
Dec 17 Javascript
四十九个javascript小知识实用技巧
Nov 20 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
同台服务器使用缓存APC效率高于Memcached的演示代码
2010/02/16 PHP
hessian 在PHP中的使用介绍
2010/12/13 PHP
PHP实现的QQ空间g_tk加密算法
2015/07/09 PHP
php判断文件上传图片格式的实例详解
2017/09/30 PHP
纯js实现的论坛常用的运行代码的效果
2008/07/15 Javascript
学习ExtJS table布局
2009/10/08 Javascript
文字溢出实现溢出的部分再放入一个新生成的div中具体代码
2013/05/17 Javascript
用javascript添加控件自定义属性解析
2013/11/25 Javascript
js实现DOM走马灯特效的方法
2015/01/21 Javascript
Jquery 实现图片轮换
2015/01/28 Javascript
全面解析Bootstrap表单使用方法(表单样式)
2015/11/24 Javascript
JavaScript获取当前cpu使用率的方法
2015/12/15 Javascript
jquery自定义右键菜单、全选、不连续选择
2016/03/01 Javascript
跨域请求的完美解决方法(JSONP, CORS)
2016/06/12 Javascript
jQuery实现select模糊查询(反射机制)
2017/01/14 Javascript
jQuery点击头像上传并预览图片
2017/02/23 Javascript
JS操作input标签属性checkbox全选的实现代码
2017/03/02 Javascript
用vue和node写的简易购物车实现
2017/04/25 Javascript
使用vue-router设置每个页面的title方法
2018/02/11 Javascript
在vue项目中引用Iview的方法
2018/09/14 Javascript
实例分析vue循环列表动态数据的处理方法
2018/09/28 Javascript
JavaScript实现页面中录音功能的方法
2019/06/04 Javascript
Python实现telnet服务器的方法
2015/07/10 Python
python实现录音小程序
2020/10/26 Python
numpy给array增加维度np.newaxis的实例
2018/11/01 Python
面向对象学习之pygame坦克大战
2019/09/11 Python
在python中创建指定大小的多维数组方式
2019/11/28 Python
python属于哪种语言
2020/08/16 Python
关于python中导入文件到list的问题
2020/10/31 Python
python cookie反爬处理的实现
2020/11/01 Python
Bandier官网:奢侈、时尚前卫的健身服装首选目的地
2020/07/05 全球购物
家佳咖啡店创业计划书
2013/12/27 职场文书
村干部群众路线教育活动对照检查材料
2014/10/01 职场文书
教师节感谢信
2015/01/22 职场文书
司机岗位职责范本
2015/04/10 职场文书
实习员工转正的评语汇总,以备不时之需
2019/12/17 职场文书