• 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-areamovable-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;
        }
      }
    }

    好了,小程序左滑右滑效果就说到这里了,如果大家有更好的办法请在下方留言,如果有什么不懂的可以在下面提问,有时间我会一一回复的,谢谢了!

    附加:由于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数据包含组件临时数据
        pos 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({
            pos 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({
            pos 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,
                pos 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({
            pos 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,
          // 图片位移效果
          pos -500,
          posheight: 100,
          dxangle: -20,
          opacity: 0,
          animation: true,
        }, () => {
          setTimeout( () => {
            this.setState({
              // 删除第一张图片和重置样式
              currentPage: this.state.currentPage+1,
              pos 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,
          // 图片位移效果
          pos 500,
          posheight: 100,
          dxangle: 20,
          opacity: 0,
          animation: true,
        }, () => {
          setTimeout( () => {
            this.setState({
              // 删除第一张图片和重置样式
              currentPage: this.state.currentPage+1,
              pos 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;
        }
      }
    }  

    不论什么框架,代码都是相通的,看上方源码了解方法和逻辑就可以都适用了。

    跋尾:文章发布没多久,去百度上检索就发现我的知识成果迅速被剽窃了,由于不是博客园内部文章,相关工作人员也管不了,所以,只能在这里对剽窃别人知识成果的行为表示深深的鄙视,地址就不贴出来了,免得给他们增加流量;

  • 相关阅读:
    空间距离计算
    一种支持多种并行环境的栅格地理计算并行算子
    发布或重启线上服务时抖动问题解决方案
    jetty9优化的两处地方
    mysql空间扩展 VS PostGIS
    多流向算法GPU并行化
    GDAL并行I/O
    深入浅出空间索引:2
    深入浅出空间索引:为什么需要空间索引
    virtualBox中的centOS虚拟机硬盘扩容
  • 原文地址:https://www.cnblogs.com/hejun26/p/12895088.html
Copyright © 2020-2023  润新知