React+Taro开发小程序实现左滑喜欢右滑不喜欢效果
序言:
年后入职了一家新公司,与前同事交接完之后,发现公司有一个四端的项目(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; } } }
好了,小程序左滑右滑效果就说到这里了,如果大家有更好的办法请在下方留言,如果有什么不懂的可以在下面提问,有时间我会一一回复的,谢谢了!
第二种实现方式:
更优解决方法
GIF效果如下:
附加:由于MovableView目前只支持小程序,并且图片无法完全滑出页面,h5不太适用,接下来提供第二种方法,效果比第一种方法更好,直接贴源码
import Taro, { Component } from '@tarojs/taro'; import { View, Image, Button, Text } from '@tarojs/components'; import { observer, inject } from '@tarojs/mobx'; import { AtButton, AtFloatLayout } from 'taro-ui'; import { userRecommend, userFavorite, userFavoHistory } from '../../api/index'; import userStore from '../../store/user.store'; import './stroll.scss'; @inject('userStore') @observer class Stroll extends Component { config = { navigationBarTitleText: '逛', disableScroll: true, } state = { // 列表参数 lastId: '', count: 20, // 推荐商品数组 cardshow: false, shopList: [{skuId:'',skuPicture:''}], // 历史浏览条数 browseCount: '', recommendCount: '', // 按钮显示状态 dislikeshow: true, likeshow: true, // 提示弹窗状态 popupshow: false, popupstate: 0, // basicdata数据包含组件基本数据 startx: '', // 记录起始位置 starty: '', // 记录起始位置 endx: '', // 记录终点位置 endy: '', // 记录终点位置 currentPage: 0, // 默认首图的序列 // temporaryData数据包含组件临时数据 poswidth: 0, // 记录位移 posheight: 0, // 记录位移 dxangle: 0, // 拖拽角度 tracking: false, // 是否在滑动,防止多次操作,影响体验 animation: false, // 首图是否启用动画效果,默认为否 opacity: 1, // 记录首图透明度 } componentWillMount () { } componentWillReact () { } componentDidShow () { // 用户推荐列表 this.recommendList(); // 判断用户是否是第一次进入该页面,储存一个判断值 if (Taro.getStorageSync('stepState') === '') { this.setState({ popupshow: true, popupstate: 1, }); } } // 推荐列表接口 async recommendList() { // 第一次进来需要调用加载图 if (this.state.cardshow === false) { Taro.showLoading(); } const [err, res] = await userRecommend({ lastId: this.state.lastId, count:this.state.count, }); Taro.hideLoading(); if (err) { return; } if (!err&&res&&res.data) { this.setState({ // 显示状态 cardshow: true, currentPage: 0, // 历史浏览条数 browseCount: res.data.browseCount, recommendCount: res.data.recommendCount, // 图片数组 shopList: res.data.record, }); } } // 进入页面执行 componentDidMount() { //登录并且没有选择性别 setTimeout(() => { if(this.props.userStore.hasLogin&&!this.props.userStore.hasChoiceSex){ Taro.navigateTo({ url:'/sub-pages/gender/gender', }); } },1500); } componentWillUnmount () {} componentDidHide () { console.log('11111',this.state.currentPage,this.state.shopList); this.setState({ // 数组计算 shopList: this.state.shopList.splice(this.state.currentPage,100), },() => { this.setState({ // 序列清零 currentPage: 0, },() => { console.log('2222',this.state.currentPage,this.state.shopList); }); }); } // 首页样式切换 transformIndex (index,color) { // console.log('transformIndex', index); // 处理3D效果 if (index === this.state.currentPage) { let style = {}; style['transform'] = 'translate3D(' + this.state.poswidth + 'px' + ',' + this.state.posheight + 'px' + ',0px)'+ 'rotate(' + this.state.dxangle + 'deg)'; style['opacity'] = this.state.opacity; style['zIndex'] = 10; style['box-shadow'] = '0px 1px 20px 0px rgba(0,0,0,0.1)'; style['background'] = color; if (this.state.animation) { style['transitionTimingFunction'] = 'ease'; style['transitionDuration'] = 400 + 'ms'; } // console.log('style1', style); return style; } } // 非首页样式切换 transform (index,color) { // console.log('transform', index); if (index > this.state.currentPage) { let style = {}; let visible = 3; let perIndex = index - this.state.currentPage; // visible可见数量前滑块的样式 if (index <= this.state.currentPage + visible) { style['opacity'] = '1'; style['transform'] = 'translate3D(0,0,' + -1 * perIndex * 10 + 'px' + ')'; style['zIndex'] = visible - index + this.state.currentPage; style['transitionTimingFunction'] = 'ease'; style['transitionDuration'] = 400 + 'ms'; style['box-shadow'] = '0px 1px 20px 0px rgba(0,0,0,0.1)'; style['background'] = color; } else { style['zIndex'] = '-1'; style['transform'] = 'translate3D(0,0,' + -1 * visible * 10 + 'px' + ')'; style['background'] = color; } // console.log('==========', index, this.state.currentPage); // console.log('style2', style); return style; } else { let style = {}; style['opacity'] = '0'; style['transitionDuration'] = '0ms'; // console.log('+++++++++++', index, this.state.currentPage); // console.log('style3', style); return style; } } //触摸开始 touchstart (e) { e.preventDefault(); // console.log('touchstart', e); // 是否在滑动 if (this.state.tracking) { return; } // 是否为touch if (e.type === 'touchstart') { if (e.touches.length > 1) { // console.log('触摸开始-----------------------touches.length > 1'); this.setState({ tracking: false, }); return; } else { // console.log('触摸开始-----------------------startxstarty'); // 记录起始位置 this.setState({ startx: e.changedTouches[0].clientX, starty: e.changedTouches[0].clientY, }); this.setState({ endx: e.changedTouches[0].clientX, endy: e.changedTouches[0].clientY, }); } } this.setState({ tracking: true, animation: false, }); // console.log('1011'); } // 触摸移动 touchmove (e) { e.preventDefault(); // console.log('touchmove', e); // 记录滑动位置 if (this.state.tracking && !this.state.animation) { // console.log('触摸移动-----------------------'); if (e.type === 'touchmove') { // console.log('触摸移动-----------------------endxendy'); this.setState({ endx: e.changedTouches[0].clientX, endy: e.changedTouches[0].clientY, }); } // 计算滑动值与偏移角度 this.setState({ poswidth: this.state.endx - this.state.startx, posheight: this.state.endy - this.state.starty, dxangle: (this.state.endx - this.state.startx)/10, }); // 判断移动的距离改变按钮状态 if (this.state.poswidth < -100) { this.setState({ dislikeshow: false, }); } else if (this.state.poswidth > 100) { this.setState({ likeshow: false, }); } else { this.setState({ dislikeshow: true, likeshow: true, }); } } } touchend (id,type,e) { e.preventDefault(); // console.log('touchend', e); // 是否在滑动,动画 this.setState({ tracking: false, animation: true }); // 滑动结束,触发判断 // 简单判断滑动宽度超出100像素时触发滑出 if (Math.abs(this.state.poswidth) >= 100) { // console.log('滑动宽度超出100像素时触发滑出-----------------------'); // 最终位移简单设定为x轴200像素的偏移 let ratio = Math.abs(this.state.posheight / this.state.poswidth); this.setState({ poswidth: this.state.poswidth >= 0 ? this.state.poswidth + 500 : this.state.poswidth - 500, posheight: this.state.posheight >= 0 ? Math.abs(this.state.poswidth * ratio) : -Math.abs(this.state.poswidth * ratio), opacity: 0, }, () => { setTimeout( () => { this.setState({ // 删除第一张图片和重置样式 currentPage: this.state.currentPage+1, poswidth: 0, posheight: 0, dxangle: 0, opacity: 1, animation: false, // 按钮状态 dislikeshow: true, likeshow: true, // index显示 browseCount: this.state.browseCount+1, },() => { if (this.state.currentPage === this.state.shopList.length) { this.setState({ // 重置样式 shopList: [], currentPage: 0, }); } }); },400); }); // 调用接口 if (this.state.poswidth >= 0) { // 喜欢接口 this.rightLike(id,type); } else { // 不喜欢接口 this.leftNoLike(id,type); } // 不满足条件则滑入 } else { // console.log('滑回来-----------------------'); this.setState({ poswidth: 0, posheight: 0, dxangle: 0, }); } } // 不喜欢按钮 dislikebtn() { // console.log('不喜欢'); // 不喜欢接口 this.leftNoLike(this.state.shopList[this.state.currentPage].skuId,this.state.shopList[this.state.currentPage].productSourceType); this.setState({ // 按钮状态 dislikeshow: false, // 图片位移效果 poswidth: -500, posheight: 100, dxangle: -20, opacity: 0, animation: true, }, () => { setTimeout( () => { this.setState({ // 删除第一张图片和重置样式 currentPage: this.state.currentPage+1, poswidth: 0, posheight: 0, dxangle: 0, opacity: 1, animation: false, // 按钮状态 dislikeshow: true, // index显示 browseCount: this.state.browseCount+1, },() => { if (this.state.currentPage === this.state.shopList.length) { this.setState({ // 重置样式 shopList: [], currentPage: 0, }); } }); },400); }); } // 喜欢按钮 likebtn() { console.log('喜欢'); // 喜欢接口 this.rightLike(this.state.shopList[this.state.currentPage].skuId,this.state.shopList[this.state.currentPage].productSourceType); this.setState({ // 按钮状态 likeshow: false, // 图片位移效果 poswidth: 500, posheight: 100, dxangle: 20, opacity: 0, animation: true, }, () => { setTimeout( () => { this.setState({ // 删除第一张图片和重置样式 currentPage: this.state.currentPage+1, poswidth: 0, posheight: 0, dxangle: 0, opacity: 1, animation: false, // 按钮状态 likeshow: true, // index显示 browseCount: this.state.browseCount+1, },() => { if (this.state.currentPage === this.state.shopList.length) { this.setState({ // 重置样式 shopList: [], currentPage: 0, }); } }); },400); }); } // 喜欢接口 async rightLike(id,type) { const [err, res] = await userFavorite({ skuId: id, favoriteStatus: 'LIKE', productSourceType: type, }); } // 不喜欢接口 async leftNoLike(id,type) { const [err, res] = await userFavorite({ skuId: id, favoriteStatus: 'NOT_LIKE', productSourceType: type, }); } // 查看我喜欢的 lookMyLike() { Taro.navigateTo({ url:'/stroll-pages/stroll-likelist/stroll-likelist' }); } // 下一步 stepone() { this.setState({ popupstate: 2, }); } steptwo() { this.setState({ popupstate: 3, }); } stepthree() { this.setState({ popupshow: false, }); // 改变是否第一次进入页面状态 Taro.setStorageSync('stepState', '1'); } render() { let cardList = this.state.shopList; return ( <View className='stroll-tab'> <View className='stroll-text'> <Text className='text-tip1'>慧搭脑每天为你精推荐{this.state.recommendCount}件单品</Text> <View className='text-tip2'> <Text className='t1'>左滑无感</Text> <Image src={require('./img/text_dislike.svg')} className='icon-image'></Image> <Text className='t1'>, 右滑喜欢</Text> <Image src={require('./img/text_like.svg')} className='icon-image'></Image> </View> </View> { this.state.cardshow && cardList.length !== 0 && <View className='stack'> { cardList && cardList.map((item, index) => { return( <View key={item.id} className='stack-item' style={this.transformIndex(index,item.picMainColor)||this.transform(index,item.picMainColor)} onTouchStart={this.touchstart.bind(this)} onTouchMove={this.touchmove.bind(this)} onTouchEnd={this.touchend.bind(this, item.skuId, item.productSourceType)} > <View className='item-data'> <Image className='imgs' mode='aspectFit' src={item.skuPicture}></Image> </View> </View> ); }) } <View className='card-num'>{this.state.browseCount+1}/{this.state.recommendCount}</View> <View className='card-btn'> { this.state.dislikeshow ? (<Image src={require('./img/dislike_default.png')} className='dislike-image' onClick={this.dislikebtn.bind(this)}></Image>): (<Image src={require('./img/dislike_light.png')} className='dislike-image'></Image>) } { this.state.likeshow ? (<Image src={require('./img/like_default.png')} className='like-image' onClick={this.likebtn.bind(this)}></Image>): (<Image src={require('./img/like_light.png')} className='like-image'></Image>) } </View> </View> } { this.state.cardshow && cardList.length === 0&& <View className='noshop-card'> <Image src='https://edgefix-image.edgecom.top/403EC3F8835C5521A500F04F35E3ADF7.png' className='noshop-image' mode='widthFix'></Image> </View> } <View className='fotter-box'> <Text className='fot-text' onClick={this.lookMyLike.bind(this)}>查看我喜欢的</Text> </View> { this.state.popupshow&& <View className='popup-box'> { this.state.popupstate === 1 && <Image src='https://edgefix-image.edgecom.top/F8BEDD2B180FE71C05F7B6E461ECB759.png' className='stepimage1' onClick={this.stepone.bind(this)}></Image> } { this.state.popupstate === 2 && <Image src='https://edgefix-image.edgecom.top/387AF72683F8598EACA618F1B95A0C23.png' className='stepimage2' onClick={this.steptwo.bind(this)}></Image> } { this.state.popupstate === 3 && <Image src='https://edgefix-image.edgecom.top/90C93EEE270440031187CFDC917DF9AC.png' className='stepimage3' onClick={this.stepthree.bind(this)}></Image> } </View> } </View> ); } } export default Stroll;
page { height: 100%; background: #F6F6F6; } .stroll-tab { width: 100%; height: 100vh; background: #F6F6F6; position: relative; overflow-x: hidden; overflow-y: hidden; .stroll-text { width: 100%; padding-top: 40px; padding-bottom: 20px; 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; margin-left: 4px; } } } .stack { width: 100%; height: 72.072072vh; position: relative; perspective: 1000px; //子元素视距 perspective-origin: 50% 150%; //子元素透视位置 -webkit-perspective: 1000px; -webkit-perspective-origin: 50% 150%; margin: 0; padding: 0; .stack-item{ background: #fff; width: 686px; height: 72.072072vh; border-radius: 24px; overflow: hidden; position: absolute; left: 32px; top: 0px; .item-data { position: relative; width: 100%; height: 100%; .imgs { position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; display: block; pointer-events: none; } } } .card-num { position: absolute; right: 64px; top: 40px; width: 144px; height: 64px; background: rgba(0,0,0,0.3); border-radius: 32px; z-index: 100; font-size: 32px; color: #FFFFFF; text-align: center; line-height: 64px; } .card-btn { position: absolute; left: 0px; bottom: 48px; z-index: 20; width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: center; .dislike-image { width: 120px; height: 120px; border-radius: 50%; box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.05); } .like-image { width: 120px; height: 120px; margin-left: 48px; border-radius: 50%; box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.05); } } } .noshop-card { width: 100%; padding: 0px 16px; .noshop-image { width: 100%; } } .fotter-box { width: 100%; text-align: center; position: absolute; bottom: 20px; left: 0px; .fot-text { color: #368BE5; font-size: 28px; padding: 10px 20px; } } .popup-box { width: 100%; height: 100vh; position: absolute; left: 0; top: 0; background:rgba(0,0,0,0.5); .stepimage1 { width: 100%; height: 344px; margin-top: 36.036036vh; } .stepimage2 { width: 100%; height: 300px; margin-top: 68.468468vh; } .stepimage3 { width: 100%; height: 300px; margin-top: 68.468468vh; } } }
不论什么框架,代码都是相通的,看上方源码了解方法和逻辑就可以都适用了。