From 089a3bc6b91dd40ae79f32471af7bf8e92edf134 Mon Sep 17 00:00:00 2001 From: DCloud_LXH <283700113@qq.com> Date: Tue, 20 Sep 2022 18:17:39 +0800 Subject: [PATCH] chore: movable-view logical reorganization --- .../src/vue/movable-view/index.tsx | 810 +++++++++++------- 1 file changed, 500 insertions(+), 310 deletions(-) diff --git a/packages/uni-components/src/vue/movable-view/index.tsx b/packages/uni-components/src/vue/movable-view/index.tsx index 389f4eac7..a00640c09 100644 --- a/packages/uni-components/src/vue/movable-view/index.tsx +++ b/packages/uni-components/src/vue/movable-view/index.tsx @@ -129,27 +129,23 @@ type ScaleOffset = { } type MoveDirection = 'htouchmove' | 'vtouchmove' -function useMovableViewState( - props: Props, - trigger: CustomEventTrigger, - rootRef: RootRef +function useMovableViewLayout( + rootRef: RootRef, + _scale: Ref, + _adjustScale: (scale: number) => number ) { const movableAreaWidth: Ref = inject('movableAreaWidth', ref(0)) const movableAreaHeight: Ref = inject('movableAreaHeight', ref(0)) - const _isMounted: Ref = inject('_isMounted', ref(false)) const movableAreaRootRef: RootRef = inject('movableAreaRootRef')! - const addMovableViewContext: AddMovableViewContext = inject( - 'addMovableViewContext', - () => {} - ) - const removeMovableViewContext: RemoveMovableViewContext = inject( - 'removeMovableViewContext', - () => {} - ) - const xSync = ref(_getPx(props.x)) - const ySync = ref(_getPx(props.y)) - const scaleValueSync = ref(Number(props.scaleValue) || 1) + const _offset: ScaleOffset = { + x: 0, + y: 0, + } + const _scaleOffset: ScaleOffset = { + x: 0, + y: 0, + } const width = ref(0) const height = ref(0) const minX = ref(0) @@ -157,64 +153,74 @@ function useMovableViewState( const maxX = ref(0) const maxY = ref(0) - let _SFA: ReturnType_g = null - let _FA: ReturnType_g = null - const _offset: ScaleOffset = { - x: 0, - y: 0, + function _updateBoundary() { + let x = 0 - _offset.x + _scaleOffset.x + let _width = + movableAreaWidth.value - width.value - _offset.x - _scaleOffset.x + minX.value = Math.min(x, _width) + maxX.value = Math.max(x, _width) + let y = 0 - _offset.y + _scaleOffset.y + let _height = + movableAreaHeight.value - height.value - _offset.y - _scaleOffset.y + minY.value = Math.min(y, _height) + maxY.value = Math.max(y, _height) } - const _scaleOffset: ScaleOffset = { - x: 0, - y: 0, + function _updateOffset() { + _offset.x = p(rootRef.value!, movableAreaRootRef.value!) + _offset.y = f(rootRef.value!, movableAreaRootRef.value!) } - let _scale = 1 - let _oldScale = 1 - let _translateX = 0 - let _translateY = 0 - let _isScaling = false - let _isTouching = false - let __baseX: number - let __baseY: number - let _checkCanMove: boolean | null = null - let _firstMoveDirection: MoveDirection | null = null - const _declineX = new Decline() - const _declineY = new Decline() - const __touchInfo = { - historyX: [0, 0], - historyY: [0, 0], - historyT: [0, 0], + function _updateWH(scale: number) { + scale = scale || _scale.value + scale = _adjustScale(scale) + let rect = rootRef.value!.getBoundingClientRect() + height.value = rect.height / _scale.value + width.value = rect.width / _scale.value + let _height = height.value * scale + let _width = width.value * scale + _scaleOffset.x = (_width - width.value) / 2 + _scaleOffset.y = (_height - height.value) / 2 } + return { + _updateBoundary, + _updateOffset, + _updateWH, + _scaleOffset, + minX, + minY, + maxX, + maxY, + } +} + +function useMovableViewTransform( + rootRef: RootRef, + props: Props, + _scaleOffset: ScaleOffset, + _scale: Ref, + maxX: Ref, + maxY: Ref, + minX: Ref, + minY: Ref, + _translateX: Ref, + _translateY: Ref, + _SFA: ReturnType_g, + _FA: ReturnType_g, + _adjustScale: (scale: number) => number, + trigger: CustomEventTrigger +) { const dampingNumber = computed(() => { let val = Number(props.damping) return isNaN(val) ? 20 : val }) - const frictionNumber = computed(() => { - let val = Number(props.friction) - return isNaN(val) || val <= 0 ? 2 : val - }) - const scaleMinNumber = computed(() => { - let val = Number(props.scaleMin) - return isNaN(val) ? 0.5 : val - }) - const scaleMaxNumber = computed(() => { - let val = Number(props.scaleMax) - return isNaN(val) ? 10 : val - }) const xMove = computed( () => props.direction === 'all' || props.direction === 'horizontal' ) const yMove = computed( () => props.direction === 'all' || props.direction === 'vertical' ) - - const _STD = new STD( - 1, - (9 * Math.pow(dampingNumber.value, 2)) / 40, - dampingNumber.value - ) - const _friction = new Friction(1, frictionNumber.value) - + const xSync = ref(_getPx(props.x)) + const ySync = ref(_getPx(props.y)) watch( () => props.x, (val) => { @@ -233,28 +239,39 @@ function useMovableViewState( watch(ySync, (val) => { _setY(val) }) - watch( - () => props.disabled, - () => { - __handleTouchStart() - } - ) - watch( - () => props.scaleValue, - (val) => { - scaleValueSync.value = Number(val) || 0 - } + + const _STD = new STD( + 1, + (9 * Math.pow(dampingNumber.value, 2)) / 40, + dampingNumber.value ) - watch(scaleValueSync, (val) => { - _setScaleValue(val) - }) - watch(scaleMinNumber, () => { - _setScaleMinOrMax() - }) - watch(scaleMaxNumber, () => { - _setScaleMinOrMax() - }) + function _getLimitXY(x: number, y: number) { + let outOfBounds = false + if (x > maxX.value) { + x = maxX.value + outOfBounds = true + } else { + if (x < minX.value) { + x = minX.value + outOfBounds = true + } + } + if (y > maxY.value) { + y = maxY.value + outOfBounds = true + } else { + if (y < minY.value) { + y = minY.value + outOfBounds = true + } + } + return { + x, + y, + outOfBounds, + } + } function FAandSFACancel() { if (_FA) { _FA.cancel() @@ -263,39 +280,275 @@ function useMovableViewState( _SFA.cancel() } } - + function _animationTo( + x: number, + y: number, + scale: number, + source?: number | string, + r?: boolean, + o?: boolean + ) { + FAandSFACancel() + if (!xMove.value) { + x = _translateX.value + } + if (!yMove.value) { + y = _translateY.value + } + if (!props.scale) { + scale = _scale.value + } + let limitXY = _getLimitXY(x, y) + x = limitXY.x + y = limitXY.y + if (!props.animation) { + _setTransform(x, y, scale, source, r, o) + return + } + _STD._springX._solution = null + _STD._springY._solution = null + _STD._springScale._solution = null + _STD._springX._endPosition = _translateX.value + _STD._springY._endPosition = _translateY.value + _STD._springScale._endPosition = _scale.value + _STD.setEnd(x, y, scale, 1) + _SFA = g( + _STD, + function () { + let data = _STD.x() + let x = data.x + let y = data.y + let scale = data.scale + _setTransform(x, y, scale, source, r, o) + }, + function () { + _SFA!.cancel() + } + ) + } + function _setTransform( + x: number, + y: number, + scale: number, + source: string | number = '', + r?: boolean, + o?: boolean + ) { + if (!(x !== null && x.toString() !== 'NaN' && typeof x === 'number')) { + x = _translateX.value || 0 + } + if (!(y !== null && y.toString() !== 'NaN' && typeof y === 'number')) { + y = _translateY.value || 0 + } + x = Number(x.toFixed(1)) + y = Number(y.toFixed(1)) + scale = Number(scale.toFixed(1)) + if (!(_translateX.value === x && _translateY.value === y)) { + if (!r) { + trigger('change', {} as Event, { + x: v(x, _scaleOffset.x), + y: v(y, _scaleOffset.y), + source: source, + }) + } + } + if (!props.scale) { + scale = _scale.value + } + scale = _adjustScale(scale) + scale = +scale.toFixed(3) + if (o && scale !== _scale.value) { + trigger('scale', {} as Event, { + x: x, + y: y, + scale: scale, + }) + } + let transform = + 'translateX(' + + x + + 'px) translateY(' + + y + + 'px) translateZ(0px) scale(' + + scale + + ')' + rootRef.value!.style.transform = transform + rootRef.value!.style.webkitTransform = transform + _translateX.value = x + _translateY.value = y + _scale.value = scale + } + function _revise(source: number | string) { + let limitXY = _getLimitXY(_translateX.value, _translateY.value) + let x = limitXY.x + let y = limitXY.y + let outOfBounds = limitXY.outOfBounds + if (outOfBounds) { + _animationTo(x, y, _scale.value, source) + } + return outOfBounds + } function _setX(val: number) { if (xMove.value) { - if (val + _scaleOffset.x === _translateX) { + if (val + _scaleOffset.x === _translateX.value) { return _translateX } else { if (_SFA) { _SFA.cancel() } - _animationTo(val + _scaleOffset.x, ySync.value + _scaleOffset.y, _scale) + _animationTo( + val + _scaleOffset.x, + ySync.value + _scaleOffset.y, + _scale.value + ) } } return val } function _setY(val: number) { if (yMove.value) { - if (val + _scaleOffset.y === _translateY) { + if (val + _scaleOffset.y === _translateY.value) { return _translateY } else { - if (_SFA) { - _SFA.cancel() - } - _animationTo(xSync.value + _scaleOffset.x, val + _scaleOffset.y, _scale) + if (_SFA) { + _SFA.cancel() + } + _animationTo( + xSync.value + _scaleOffset.x, + val + _scaleOffset.y, + _scale.value + ) + } + } + return val + } + + return { + FAandSFACancel, + _getLimitXY, + _animationTo, + _setTransform, + _revise, + dampingNumber, + xMove, + yMove, + xSync, + ySync, + _STD, + } +} + +function useMovableViewInit( + props: Props, + rootRef: RootRef, + trigger: CustomEventTrigger, + _scale: Ref, + _oldScale: Ref, + _isScaling: Ref, + _translateX: Ref, + _translateY: Ref, + _SFA: ReturnType_g, + _FA: ReturnType_g +) { + const scaleMinNumber = computed(() => { + let val = Number(props.scaleMin) + return isNaN(val) ? 0.5 : val + }) + const scaleMaxNumber = computed(() => { + let val = Number(props.scaleMax) + return isNaN(val) ? 10 : val + }) + + const scaleValueSync = ref(Number(props.scaleValue) || 1) + watch(scaleValueSync, (val) => { + _setScaleValue(val) + }) + watch(scaleMinNumber, () => { + _setScaleMinOrMax() + }) + watch(scaleMaxNumber, () => { + _setScaleMinOrMax() + }) + watch( + () => props.scaleValue, + (val) => { + scaleValueSync.value = Number(val) || 0 + } + ) + + const { + _updateBoundary, + _updateOffset, + _updateWH, + _scaleOffset, + minX, + minY, + maxX, + maxY, + } = useMovableViewLayout(rootRef, _scale, _adjustScale) + const { + FAandSFACancel, + _getLimitXY, + _animationTo, + _setTransform, + _revise, + dampingNumber, + xMove, + yMove, + xSync, + ySync, + _STD, + } = useMovableViewTransform( + rootRef, + props, + _scaleOffset, + _scale, + maxX, + maxY, + minX, + minY, + _translateX, + _translateY, + _SFA, + _FA, + _adjustScale, + trigger + ) + + function _updateScale(scale: number, animat?: boolean) { + if (props.scale) { + scale = _adjustScale(scale) + _updateWH(scale) + _updateBoundary() + const limitXY = _getLimitXY(_translateX.value, _translateY.value) + const x = limitXY.x + const y = limitXY.y + if (animat) { + _animationTo(x, y, scale, '', true, true) + } else { + _requestAnimationFrame(function () { + _setTransform(x, y, scale, '', true, true) + }) } } - return val + } + function _beginScale() { + _isScaling.value = true + } + function _updateOldScale(scale: number) { + _oldScale.value = scale + } + function _adjustScale(scale: number) { + scale = Math.max(0.5, scaleMinNumber.value, scale) + scale = Math.min(10, scaleMaxNumber.value, scale) + return scale } function _setScaleMinOrMax() { if (!props.scale) { return false } - _updateScale(_scale, true) - _updateOldScale(_scale) + _updateScale(_scale.value, true) + _updateOldScale(_scale.value) } function _setScaleValue(scale: number) { if (!props.scale) { @@ -306,8 +559,140 @@ function useMovableViewState( _updateOldScale(scale) return scale } + function _endScale() { + _isScaling.value = false + _updateOldScale(_scale.value) + } + function _setScale(scale: number) { + if (scale) { + scale = _oldScale.value * scale + _beginScale() + _updateScale(scale) + } + } + + return { + // scale + _updateOldScale, + _endScale, + _setScale, + scaleValueSync, + + // layout + _updateBoundary, + _updateOffset, + _updateWH, + _scaleOffset, + minX, + minY, + maxX, + maxY, + + // transform + FAandSFACancel, + _getLimitXY, + _animationTo, + _setTransform, + _revise, + dampingNumber, + xMove, + yMove, + xSync, + ySync, + _STD, + } +} + +function useMovableViewState( + props: Props, + trigger: CustomEventTrigger, + rootRef: RootRef +) { + const _isMounted: Ref = inject('_isMounted', ref(false)) + const addMovableViewContext: AddMovableViewContext = inject( + 'addMovableViewContext', + () => {} + ) + const removeMovableViewContext: RemoveMovableViewContext = inject( + 'removeMovableViewContext', + () => {} + ) + let _scale = ref(1) + let _oldScale = ref(1) + let _isScaling = ref(false) + let _translateX = ref(0) + let _translateY = ref(0) + let _SFA: ReturnType_g = null + let _FA: ReturnType_g = null + + let _isTouching = false + let __baseX: number + let __baseY: number + let _checkCanMove: boolean | null = null + let _firstMoveDirection: MoveDirection | null = null + const _declineX = new Decline() + const _declineY = new Decline() + const __touchInfo = { + historyX: [0, 0], + historyY: [0, 0], + historyT: [0, 0], + } + const frictionNumber = computed(() => { + let val = Number(props.friction) + return isNaN(val) || val <= 0 ? 2 : val + }) + const _friction = new Friction(1, frictionNumber.value) + + watch( + () => props.disabled, + () => { + __handleTouchStart() + } + ) + + const { + // scale + _updateOldScale, + _endScale, + _setScale, + scaleValueSync, + + // layout + _updateBoundary, + _updateOffset, + _updateWH, + _scaleOffset, + minX, + minY, + maxX, + maxY, + + // transform + FAandSFACancel, + _getLimitXY, + _setTransform, + _revise, + dampingNumber, + xMove, + yMove, + xSync, + ySync, + _STD, + } = useMovableViewInit( + props, + rootRef, + trigger, + _scale, + _oldScale, + _isScaling, + _translateX, + _translateY, + _SFA, + _FA + ) + function __handleTouchStart() { - if (!_isScaling) { + if (!_isScaling.value) { if (!props.disabled) { disableScrollBounce({ disable: true, @@ -317,10 +702,10 @@ function useMovableViewState( __touchInfo.historyY = [0, 0] __touchInfo.historyT = [0, 0] if (xMove.value) { - __baseX = _translateX + __baseX = _translateX.value } if (yMove.value) { - __baseY = _translateY + __baseY = _translateY.value } rootRef.value!.style.willChange = 'transform' _checkCanMove = null @@ -330,9 +715,9 @@ function useMovableViewState( } } function __handleTouchMove(event: TouchtrackEvent) { - if (!_isScaling && !props.disabled && _isTouching) { - let x = _translateX - let y = _translateY + if (!_isScaling.value && !props.disabled && _isTouching) { + let x = _translateX.value + let y = _translateY.value if (_firstMoveDirection === null) { _firstMoveDirection = Math.abs(event.detail.dx / event.detail.dy) > 1 @@ -394,13 +779,13 @@ function useMovableViewState( } } _requestAnimationFrame(function () { - _setTransform(x, y, _scale, source) + _setTransform(x, y, _scale.value, source) }) } } } function __handleTouchEnd() { - if (!_isScaling && !props.disabled && _isTouching) { + if (!_isScaling.value && !props.disabled && _isTouching) { disableScrollBounce({ disable: false, }) @@ -413,28 +798,32 @@ function useMovableViewState( const yv = (1000 * (__touchInfo.historyY[1] - __touchInfo.historyY[0])) / (__touchInfo.historyT[1] - __touchInfo.historyT[0]) + + const __translateX = _translateX.value + const __translateY = _translateY.value + _friction.setV(xv, yv) - _friction.setS(_translateX, _translateY) + _friction.setS(__translateX, __translateY) const x0 = _friction.delta().x const y0 = _friction.delta().y - let x = x0 + _translateX - let y = y0 + _translateY + let x = x0 + __translateX + let y = y0 + __translateY if (x < minX.value) { x = minX.value - y = _translateY + ((minX.value - _translateX) * y0) / x0 + y = __translateY + ((minX.value - __translateX) * y0) / x0 } else { if (x > maxX.value) { x = maxX.value - y = _translateY + ((maxX.value - _translateX) * y0) / x0 + y = __translateY + ((maxX.value - __translateX) * y0) / x0 } } if (y < minY.value) { y = minY.value - x = _translateX + ((minY.value - _translateY) * x0) / y0 + x = __translateX + ((minY.value - __translateY) * x0) / y0 } else { if (y > maxY.value) { y = maxY.value - x = _translateX + ((maxY.value - _translateY) * x0) / y0 + x = __translateX + ((maxY.value - __translateY) * x0) / y0 } } _friction.setEnd(x, y) @@ -444,7 +833,7 @@ function useMovableViewState( let t = _friction.s() let x = t.x let y = t.y - _setTransform(x, y, _scale, 'friction') + _setTransform(x, y, _scale.value, 'friction') }, function () { _FA!.cancel() @@ -457,195 +846,6 @@ function useMovableViewState( FAandSFACancel() } } - function _getLimitXY(x: number, y: number) { - let outOfBounds = false - if (x > maxX.value) { - x = maxX.value - outOfBounds = true - } else { - if (x < minX.value) { - x = minX.value - outOfBounds = true - } - } - if (y > maxY.value) { - y = maxY.value - outOfBounds = true - } else { - if (y < minY.value) { - y = minY.value - outOfBounds = true - } - } - return { - x, - y, - outOfBounds, - } - } - function _updateOffset() { - _offset.x = p(rootRef.value!, movableAreaRootRef.value!) - _offset.y = f(rootRef.value!, movableAreaRootRef.value!) - } - function _updateWH(scale: number) { - scale = scale || _scale - scale = _adjustScale(scale) - let rect = rootRef.value!.getBoundingClientRect() - height.value = rect.height / _scale - width.value = rect.width / _scale - let _height = height.value * scale - let _width = width.value * scale - _scaleOffset.x = (_width - width.value) / 2 - _scaleOffset.y = (_height - height.value) / 2 - } - function _updateBoundary() { - let x = 0 - _offset.x + _scaleOffset.x - let _width = - movableAreaWidth.value - width.value - _offset.x - _scaleOffset.x - minX.value = Math.min(x, _width) - maxX.value = Math.max(x, _width) - let y = 0 - _offset.y + _scaleOffset.y - let _height = - movableAreaHeight.value - height.value - _offset.y - _scaleOffset.y - minY.value = Math.min(y, _height) - maxY.value = Math.max(y, _height) - } - function _beginScale() { - _isScaling = true - } - function _updateScale(scale: number, animat?: boolean) { - if (props.scale) { - scale = _adjustScale(scale) - _updateWH(scale) - _updateBoundary() - const limitXY = _getLimitXY(_translateX, _translateY) - const x = limitXY.x - const y = limitXY.y - if (animat) { - _animationTo(x, y, scale, '', true, true) - } else { - _requestAnimationFrame(function () { - _setTransform(x, y, scale, '', true, true) - }) - } - } - } - function _updateOldScale(scale: number) { - _oldScale = scale - } - function _adjustScale(scale: number) { - scale = Math.max(0.5, scaleMinNumber.value, scale) - scale = Math.min(10, scaleMaxNumber.value, scale) - return scale - } - function _animationTo( - x: number, - y: number, - scale: number, - source?: number | string, - r?: boolean, - o?: boolean - ) { - FAandSFACancel() - if (!xMove.value) { - x = _translateX - } - if (!yMove.value) { - y = _translateY - } - if (!props.scale) { - scale = _scale - } - let limitXY = _getLimitXY(x, y) - x = limitXY.x - y = limitXY.y - if (!props.animation) { - _setTransform(x, y, scale, source, r, o) - return - } - _STD._springX._solution = null - _STD._springY._solution = null - _STD._springScale._solution = null - _STD._springX._endPosition = _translateX - _STD._springY._endPosition = _translateY - _STD._springScale._endPosition = _scale - _STD.setEnd(x, y, scale, 1) - _SFA = g( - _STD, - function () { - let data = _STD.x() - let x = data.x - let y = data.y - let scale = data.scale - _setTransform(x, y, scale, source, r, o) - }, - function () { - _SFA!.cancel() - } - ) - } - function _revise(source: number | string) { - let limitXY = _getLimitXY(_translateX, _translateY) - let x = limitXY.x - let y = limitXY.y - let outOfBounds = limitXY.outOfBounds - if (outOfBounds) { - _animationTo(x, y, _scale, source) - } - return outOfBounds - } - function _setTransform( - x: number, - y: number, - scale: number, - source: string | number = '', - r?: boolean, - o?: boolean - ) { - if (!(x !== null && x.toString() !== 'NaN' && typeof x === 'number')) { - x = _translateX || 0 - } - if (!(y !== null && y.toString() !== 'NaN' && typeof y === 'number')) { - y = _translateY || 0 - } - x = Number(x.toFixed(1)) - y = Number(y.toFixed(1)) - scale = Number(scale.toFixed(1)) - if (!(_translateX === x && _translateY === y)) { - if (!r) { - trigger('change', {} as Event, { - x: v(x, _scaleOffset.x), - y: v(y, _scaleOffset.y), - source: source, - }) - } - } - if (!props.scale) { - scale = _scale - } - scale = _adjustScale(scale) - scale = +scale.toFixed(3) - if (o && scale !== _scale) { - trigger('scale', {} as Event, { - x: x, - y: y, - scale: scale, - }) - } - let transform = - 'translateX(' + - x + - 'px) translateY(' + - y + - 'px) translateZ(0px) scale(' + - scale + - ')' - rootRef.value!.style.transform = transform - rootRef.value!.style.webkitTransform = transform - _translateX = x - _translateY = y - _scale = scale - } function setParent() { if (!_isMounted.value) { @@ -656,25 +856,15 @@ function useMovableViewState( _updateOffset() _updateWH(scale) _updateBoundary() - _translateX = xSync.value + _scaleOffset.x - _translateY = ySync.value + _scaleOffset.y - let limitXY = _getLimitXY(_translateX, _translateY) + let limitXY = _getLimitXY( + xSync.value + _scaleOffset.x, + ySync.value + _scaleOffset.y + ) let x = limitXY.x let y = limitXY.y _setTransform(x, y, scale, '', true) _updateOldScale(scale) } - function _endScale() { - _isScaling = false - _updateOldScale(_scale) - } - function _setScale(scale: number) { - if (scale) { - scale = _oldScale * scale - _beginScale() - _updateScale(scale) - } - } onMounted(() => { useTouchtrack(rootRef.value!, (event) => { -- GitLab