diff --git a/packages/vant/src/swipe/README.md b/packages/vant/src/swipe/README.md index 4f4bd19eb56..02185a00e62 100644 --- a/packages/vant/src/swipe/README.md +++ b/packages/vant/src/swipe/README.md @@ -137,6 +137,17 @@ export default { ``` +### RTL Layout + +```html + + 1 + 2 + 3 + 4 + +``` + ## API ### Swipe Props diff --git a/packages/vant/src/swipe/README.zh-CN.md b/packages/vant/src/swipe/README.zh-CN.md index d7aeb207008..ab8e1278d62 100644 --- a/packages/vant/src/swipe/README.zh-CN.md +++ b/packages/vant/src/swipe/README.zh-CN.md @@ -145,6 +145,19 @@ export default { ``` +### RTL 布局 + +可以通过对上下文或自身样式的direction属性判断调整。 + +```html + + 1 + 2 + 3 + 4 + +``` + ## API ### Swipe Props diff --git a/packages/vant/src/swipe/Swipe.tsx b/packages/vant/src/swipe/Swipe.tsx index 370c87af2d9..08f4a9a7d28 100644 --- a/packages/vant/src/swipe/Swipe.tsx +++ b/packages/vant/src/swipe/Swipe.tsx @@ -25,6 +25,7 @@ import { preventDefault, createNamespace, makeNumericProp, + isRtl, } from '../utils'; // Composables @@ -83,6 +84,7 @@ export default defineComponent({ // Whether the user is dragging the swipe let dragging = false; + let rtl = false; const touch = useTouch(); const { children, linkChildren } = useChildren(SWIPE_KEY); @@ -98,7 +100,9 @@ export default defineComponent({ const minOffset = computed(() => { if (state.rect) { const base = props.vertical ? state.rect.height : state.rect.width; - return base - size.value * count.value; + return rtl && !props.vertical + ? size.value * count.value - base + : base - size.value * count.value; } return 0; }); @@ -111,9 +115,9 @@ export default defineComponent({ const trackSize = computed(() => count.value * size.value); - const activeIndicator = computed( - () => (state.active + count.value) % count.value, - ); + const activeIndicator = computed(() => { + return (state.active + count.value) % count.value; + }); const isCorrectDirection = computed(() => { const expect = props.vertical ? 'vertical' : 'horizontal'; @@ -153,14 +157,21 @@ export default defineComponent({ const getTargetOffset = (targetActive: number, offset = 0) => { let currentPosition = targetActive * size.value; if (!props.loop) { - currentPosition = Math.min(currentPosition, -minOffset.value); + currentPosition = + rtl && !props.vertical + ? Math.min(currentPosition, minOffset.value) + : Math.min(currentPosition, -minOffset.value); } - - let targetOffset = offset - currentPosition; + let targetOffset = + rtl && !props.vertical + ? offset + currentPosition + : offset - currentPosition; if (!props.loop) { - targetOffset = clamp(targetOffset, minOffset.value, 0); + targetOffset = + rtl && !props.vertical + ? clamp(targetOffset, 0, minOffset.value) + : clamp(targetOffset, minOffset.value, 0); } - return targetOffset; }; @@ -180,22 +191,34 @@ export default defineComponent({ const { active } = state; const targetActive = getTargetActive(pace); const targetOffset = getTargetOffset(targetActive, offset); - // auto move first and last swipe in loop mode if (props.loop) { - if (children[0] && targetOffset !== minOffset.value) { - const outRightBound = targetOffset < minOffset.value; - children[0].setOffset(outRightBound ? trackSize.value : 0); - } + if (rtl && !props.vertical) { + if (children[count.value - 1]) { + const outRightBound = targetOffset < size.value; + children[count.value - 1].setOffset( + outRightBound ? trackSize.value : 0, + ); + } - if (children[count.value - 1] && targetOffset !== 0) { - const outLeftBound = targetOffset > 0; - children[count.value - 1].setOffset( - outLeftBound ? -trackSize.value : 0, - ); + if (children[0]) { + const outLeftBound = targetOffset >= minOffset.value; + children[0].setOffset(outLeftBound ? -trackSize.value : 0); + } + } else { + if (children[0] && targetOffset !== minOffset.value) { + const outRightBound = targetOffset < minOffset.value; + children[0].setOffset(outRightBound ? trackSize.value : 0); + } + + if (children[count.value - 1] && targetOffset !== 0) { + const outLeftBound = targetOffset > 0; + children[count.value - 1].setOffset( + outLeftBound ? -trackSize.value : 0, + ); + } } } - state.active = targetActive; state.offset = targetOffset; @@ -281,6 +304,7 @@ export default defineComponent({ } } + rtl = isRtl(root); state.active = active; state.swiping = true; state.offset = getTargetOffset(active); @@ -327,9 +351,13 @@ export default defineComponent({ if (isCorrectDirection.value) { const isEdgeTouch = !props.loop && - ((state.active === 0 && delta.value > 0) || - (state.active === count.value - 1 && delta.value < 0)); - + ((!rtl && + ((state.active === 0 && delta.value > 0) || + (state.active === count.value - 1 && delta.value < 0))) || + (rtl && + ((state.active === count.value - 1 && delta.value > 0) || + (state.active === 0 && delta.value < 0)) && + !props.vertical)); if (!isEdgeTouch) { preventDefault(event, props.stopPropagation); move({ offset: delta.value }); @@ -357,16 +385,19 @@ export default defineComponent({ const offset = props.vertical ? touch.offsetY.value : touch.offsetX.value; - let pace = 0; - if (props.loop) { - pace = offset > 0 ? (delta.value > 0 ? -1 : 1) : 0; + if (offset > 0) { + pace = delta.value > 0 ? -1 : 1; + } else { + pace = 0; + } } else { pace = -Math[delta.value > 0 ? 'ceil' : 'floor']( delta.value / size.value, ); } + pace = rtl && !props.vertical ? -pace : pace; move({ pace, @@ -430,7 +461,12 @@ export default defineComponent({ } if (props.showIndicators && count.value > 1) { return ( -
+
{Array(count.value).fill('').map(renderDot)}
); diff --git a/packages/vant/src/swipe/demo/index.vue b/packages/vant/src/swipe/demo/index.vue index 5b33fbd8e8d..d049dfef054 100644 --- a/packages/vant/src/swipe/demo/index.vue +++ b/packages/vant/src/swipe/demo/index.vue @@ -11,6 +11,7 @@ const t = useTranslate({ title4: '纵向滚动', title5: '自定义滑块大小', title6: '自定义指示器', + title7: 'RTL 布局', message: '当前 Swipe 索引:', }, 'en-US': { @@ -19,6 +20,7 @@ const t = useTranslate({ title4: 'Vertical Scrolling', title5: 'Set SwipeItem Size', title6: 'Custom indicator', + title7: ' RTL Layout', message: 'Current Swipe index:', }, }); @@ -95,6 +97,15 @@ const onChange = (index: number) => showToast(t('message') + index); + + + + 1 + 2 + 3 + 4 + +