序言:
年后入職了一家新公司,與前同事交接完之后,發現公司有一個四端的項目(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; } } }
不論什么框架,代碼都是相通的,看上方源碼了解方法和邏輯就可以都適用了。