| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- <template>
- <view
- class="sliding-container"
- :style="containerStyle"
- @touchstart="touchStart"
- @touchmove="touchMove"
- @touchend="touchEnd"
- >
- <!-- 上一页内容 -->
- <view class="page prev-page" :style="prevPageStyle">
- <slot name="prev"></slot>
- </view>
-
- <!-- 当前页内容 -->
- <view class="page current-page" :style="currentPageStyle">
- <slot name="current"></slot>
- </view>
-
- <!-- 下一页内容 -->
- <view class="page next-page" :style="nextPageStyle">
- <slot name="next"></slot>
- </view>
- </view>
- </template>
- <script>
- const SWIPE_THRESHOLD = 50; // 滑动阈值
- const ANIMATION_DURATION = 300; // 动画持续时间(毫秒)
- export default {
- name: 'SlidingContainer',
- data() {
- return {
- touchStartX: 0,
- touchStartY: 0,
- deltaX: 0,
- deltaY: 0,
- isAnimating: false,
- containerWidth: 0,
- }
- },
- props: {
- // 是否禁用滑动
- disabled: {
- type: Boolean,
- default: false
- }
- },
- computed: {
- containerStyle() {
- return {
- transform: `translateX(${this.deltaX}px)`,
- transition: this.isAnimating ? `transform ${ANIMATION_DURATION}ms ease-out` : 'none'
- }
- },
- prevPageStyle() {
- return {
- transform: 'translateX(-100%)',
- }
- },
- currentPageStyle() {
- return {
- transform: 'translateX(0)',
- }
- },
- nextPageStyle() {
- return {
- transform: 'translateX(100%)',
- }
- }
- },
- mounted() {
- // 获取容器宽度
- const query = uni.createSelectorQuery().in(this);
- query.select('.sliding-container').boundingClientRect(data => {
- if (data) {
- this.containerWidth = data.width;
- }
- }).exec();
- },
- methods: {
- touchStart(e) {
- if (this.disabled || this.isAnimating) return;
-
- const touch = e.touches[0];
- this.touchStartX = touch.clientX;
- this.touchStartY = touch.clientY;
- this.deltaX = 0;
- this.deltaY = 0;
- },
- touchMove(e) {
- if (this.disabled || this.isAnimating) return;
-
- const touch = e.touches[0];
- this.deltaX = touch.clientX - this.touchStartX;
- this.deltaY = touch.clientY - this.touchStartY;
-
- // 如果垂直滑动距离大于水平滑动距离,则不处理
- if (Math.abs(this.deltaY) > Math.abs(this.deltaX)) {
- return;
- }
-
- // 阻止默认行为
- e.preventDefault();
-
- // 添加阻尼效果
- const damping = 0.5;
- this.deltaX = this.deltaX * damping;
- },
- touchEnd() {
- if (this.disabled || this.isAnimating) return;
-
- // 判断滑动方向和距离
- if (Math.abs(this.deltaX) > SWIPE_THRESHOLD) {
- if (this.deltaX > 0) {
- this.slideToPrev();
- } else {
- this.slideToNext();
- }
- } else {
- this.resetPosition();
- }
- },
- slideToPrev() {
- this.isAnimating = true;
- this.deltaX = this.containerWidth;
-
- setTimeout(() => {
- this.isAnimating = false;
- this.deltaX = 0;
- this.$emit('slide-prev');
- }, ANIMATION_DURATION);
- },
- slideToNext() {
- this.isAnimating = true;
- this.deltaX = -this.containerWidth;
-
- setTimeout(() => {
- this.isAnimating = false;
- this.deltaX = 0;
- this.$emit('slide-next');
- }, ANIMATION_DURATION);
- },
- resetPosition() {
- this.isAnimating = true;
- this.deltaX = 0;
-
- setTimeout(() => {
- this.isAnimating = false;
- }, ANIMATION_DURATION);
- }
- }
- }
- </script>
- <style>
- .sliding-container {
- position: relative;
- width: 100%;
- height: 100%;
- min-height: 100vh;
- overflow: hidden;
- touch-action: pan-y pinch-zoom;
- background-color: inherit;
- }
- .page {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- min-height: 100vh;
- will-change: transform;
- background-color: inherit;
- overflow-y: auto;
- }
- .prev-page {
- z-index: 1;
- }
- .current-page {
- z-index: 2;
- }
- .next-page {
- z-index: 1;
- }
- </style>
|