序言:
年后入职了一家新公司,与前同事交接完之后,发现公司有一个四端的项目(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;
}
}
}不论什么框架,代码都是相通的,看上方源码了解方法和逻辑就可以都适用了。
















