|
|
@@ -21,6 +21,7 @@
|
|
|
:acceleration="true"
|
|
|
:circular="swiperCircular"
|
|
|
:current="activeIndex"
|
|
|
+ :disable-touch="goodsTouchLock"
|
|
|
:style="rootStyle"
|
|
|
@change="onSlideChange"
|
|
|
>
|
|
|
@@ -184,28 +185,23 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <!-- 挂商品;仅当前页渲染,避免 iOS 邻页占位撑乱布局 -->
|
|
|
+ <!-- 挂商品:自定义横滑(iOS 竖滑 swiper 内 scroll-view 常被抢走手势) -->
|
|
|
<view
|
|
|
v-if="isActiveSlide(index) && goodsListOf(clip).length > 0"
|
|
|
class="reel-goods"
|
|
|
@click.stop
|
|
|
+ @touchstart="onGoodsPanStart"
|
|
|
+ @touchmove="onGoodsPanMove"
|
|
|
+ @touchend="onGoodsPanEnd"
|
|
|
+ @touchcancel="onGoodsPanEnd"
|
|
|
>
|
|
|
- <scroll-view
|
|
|
- class="reel-goods-scroll"
|
|
|
- scroll-x="true"
|
|
|
- :enable-flex="true"
|
|
|
- :show-scrollbar="false"
|
|
|
- :style="{ width: frameW + 'px' }"
|
|
|
- @touchmove.stop
|
|
|
- >
|
|
|
- <view class="reel-goods-row">
|
|
|
+ <view class="reel-goods-viewport" :style="{ width: frameW + 'px' }">
|
|
|
+ <view class="reel-goods-track" :style="goodsTrackStyle(clip)">
|
|
|
<view
|
|
|
v-for="(goods, gIndex) in goodsListOf(clip)"
|
|
|
:key="goods.id || gIndex"
|
|
|
class="reel-goods-item"
|
|
|
- :style="{ width: Math.round(frameW * 0.8) + 'px' }"
|
|
|
- @touchstart="onGoodsTouchStart"
|
|
|
- @touchmove="onGoodsTouchMove"
|
|
|
+ :style="{ width: goodsCardWidth + 'px' }"
|
|
|
@tap="onGoodsItemTap(goods)"
|
|
|
>
|
|
|
<image
|
|
|
@@ -223,8 +219,9 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
+ <view class="reel-goods-tail" />
|
|
|
</view>
|
|
|
- </scroll-view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -545,6 +542,10 @@ export default {
|
|
|
/** 强制重建 swiper / 当前 video,规避 iOS 首条黑屏 */
|
|
|
swiperEpoch: 0,
|
|
|
mediaEpoch: 0,
|
|
|
+ /** 触摸商品条时锁定竖滑,避免 iOS 抢走横向滑动 */
|
|
|
+ goodsTouchLock: false,
|
|
|
+ /** 商品条横向偏移(自定义横滑) */
|
|
|
+ goodsOffsetX: 0,
|
|
|
// ─── 评论弹层 ───
|
|
|
commentVisible: false,
|
|
|
commentLoading: false,
|
|
|
@@ -723,6 +724,9 @@ export default {
|
|
|
} catch (_) {
|
|
|
return false
|
|
|
}
|
|
|
+ },
|
|
|
+ goodsCardWidth() {
|
|
|
+ return Math.round((Number(this.frameW) || 0) * 0.8)
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
@@ -1042,25 +1046,71 @@ export default {
|
|
|
const path = goods && (goods.image || goods.image_url || goods.cover)
|
|
|
return path ? buildOssUrl(path, 120, 120) : ''
|
|
|
},
|
|
|
- /** 商品卡:区分横滑与点击,避免 @tap.stop 抢走 scroll-view 滑动 */
|
|
|
- onGoodsTouchStart(e) {
|
|
|
+ goodsTrackWidth(clip) {
|
|
|
+ const n = this.goodsListOf(clip).length
|
|
|
+ if (!n) return 0
|
|
|
+ const gap = uni.upx2px(14)
|
|
|
+ const tail = uni.upx2px(24)
|
|
|
+ return n * (this.goodsCardWidth + gap) + tail
|
|
|
+ },
|
|
|
+ goodsMinOffset(clip) {
|
|
|
+ return Math.min(0, (Number(this.frameW) || 0) - this.goodsTrackWidth(clip))
|
|
|
+ },
|
|
|
+ goodsTrackStyle(clip) {
|
|
|
+ const x = this.goodsOffsetX
|
|
|
+ return {
|
|
|
+ width: this.goodsTrackWidth(clip) + 'px',
|
|
|
+ transform: `translateX(${x}px)`,
|
|
|
+ webkitTransform: `translateX(${x}px)`
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onGoodsPanStart(e) {
|
|
|
const t = e && e.touches && e.touches[0]
|
|
|
- this._goodsTouchX = t ? t.pageX : 0
|
|
|
- this._goodsTouchY = t ? t.pageY : 0
|
|
|
- this._goodsTouchMoved = false
|
|
|
+ this._gStartX = t ? t.pageX : 0
|
|
|
+ this._gStartY = t ? t.pageY : 0
|
|
|
+ this._gBase = this.goodsOffsetX
|
|
|
+ this._gAxis = ''
|
|
|
+ this._gMoved = false
|
|
|
},
|
|
|
- onGoodsTouchMove(e) {
|
|
|
+ onGoodsPanMove(e) {
|
|
|
const t = e && e.touches && e.touches[0]
|
|
|
if (!t) return
|
|
|
- if (
|
|
|
- Math.abs(t.pageX - this._goodsTouchX) > 8 ||
|
|
|
- Math.abs(t.pageY - this._goodsTouchY) > 8
|
|
|
- ) {
|
|
|
- this._goodsTouchMoved = true
|
|
|
+ const dx = t.pageX - this._gStartX
|
|
|
+ const dy = t.pageY - this._gStartY
|
|
|
+ if (!this._gAxis) {
|
|
|
+ if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return
|
|
|
+ this._gAxis = Math.abs(dx) >= Math.abs(dy) ? 'x' : 'y'
|
|
|
+ }
|
|
|
+ if (this._gAxis !== 'x') {
|
|
|
+ this.goodsTouchLock = false
|
|
|
+ return
|
|
|
}
|
|
|
+ this.goodsTouchLock = true
|
|
|
+ this._gMoved = true
|
|
|
+ if (e && e.cancelable && typeof e.preventDefault === 'function') {
|
|
|
+ e.preventDefault()
|
|
|
+ }
|
|
|
+ const clip = this.clips[this.slideIndex(this.activeIndex)]
|
|
|
+ const min = this.goodsMinOffset(clip)
|
|
|
+ let next = this._gBase + dx
|
|
|
+ if (next > 0) next *= 0.35
|
|
|
+ else if (next < min) next = min + (next - min) * 0.35
|
|
|
+ this.goodsOffsetX = next
|
|
|
+ },
|
|
|
+ onGoodsPanEnd() {
|
|
|
+ const clip = this.clips[this.slideIndex(this.activeIndex)]
|
|
|
+ const min = this.goodsMinOffset(clip)
|
|
|
+ if (this.goodsOffsetX > 0) this.goodsOffsetX = 0
|
|
|
+ else if (this.goodsOffsetX < min) this.goodsOffsetX = min
|
|
|
+ this.goodsTouchLock = false
|
|
|
+ // 延后清除,避免 touchend 后立刻触发 tap
|
|
|
+ setTimeout(() => {
|
|
|
+ this._gMoved = false
|
|
|
+ this._gAxis = ''
|
|
|
+ }, 50)
|
|
|
},
|
|
|
onGoodsItemTap(goods) {
|
|
|
- if (this._goodsTouchMoved) return
|
|
|
+ if (this._gMoved) return
|
|
|
this.onGoodsTap(goods)
|
|
|
},
|
|
|
onGoodsTap(goods) {
|
|
|
@@ -1822,6 +1872,8 @@ export default {
|
|
|
onSlideChange(e) {
|
|
|
const next = this.slideIndex(e && e.detail && e.detail.current)
|
|
|
if (next === this.slideIndex(this.activeIndex)) return
|
|
|
+ this.goodsTouchLock = false
|
|
|
+ this.goodsOffsetX = 0
|
|
|
this.circularUnlocked = true
|
|
|
this.haltAll()
|
|
|
if (this.seriesSheetVisible) this.closeSeriesSheet()
|
|
|
@@ -2417,23 +2469,21 @@ export default {
|
|
|
z-index: 9;
|
|
|
margin-top: 16rpx;
|
|
|
}
|
|
|
-.reel-goods-scroll {
|
|
|
+.reel-goods-viewport {
|
|
|
height: 174rpx;
|
|
|
- /* #ifndef APP-PLUS */
|
|
|
- white-space: nowrap;
|
|
|
- /* #endif */
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
-.reel-goods-row {
|
|
|
+.reel-goods-track {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
- align-items: stretch;
|
|
|
+ align-items: center;
|
|
|
height: 174rpx;
|
|
|
- padding-right: 24rpx;
|
|
|
}
|
|
|
.reel-goods-item {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
align-items: center;
|
|
|
+ flex-shrink: 0;
|
|
|
background-color: rgba(255, 255, 255, 0.92);
|
|
|
border-radius: 18rpx;
|
|
|
margin-left: 14rpx;
|
|
|
@@ -2441,6 +2491,11 @@ export default {
|
|
|
height: 154rpx;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
+.reel-goods-tail {
|
|
|
+ width: 24rpx;
|
|
|
+ height: 154rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
.reel-goods-img {
|
|
|
width: 126rpx;
|
|
|
height: 126rpx;
|