opentiny / tiny-vue

TinyVue is an enterprise-class UI component library of OpenTiny community, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.
https://opentiny.design/tiny-vue
MIT License
1.46k stars 241 forks source link

🐛 [Bug]: [slider] Slider component vertical mode, drag to the anchor will jump #915

Open yoyo201626 opened 8 months ago

yoyo201626 commented 8 months ago

Version

opentiny/vue@3.11.0

Vue Version

3

Link to minimal reproduction

https://opentiny.design/tiny-vue/zh-CN/os-theme/components/slider

Step to reproduce

  1. 滚动到下面 图片

  2. 点击锚点 图片

What is expected

No response

What is actually happening

No response

Any additional comments (optional)

No response

Issues-translate-bot commented 8 months ago

Bot detected the issue body's language is not English, translate it automatically.


Title: 🐛 [Bug]: Slider component in vertical mode, dragging to the anchor point will jump

Huauauaa commented 8 months ago

It seems that the component didn’t calculate the position after scroll

// renderless/src/slider/index.ts
// bindMouseDown()
+ api.bindResize()
const currentValue  = api.calculateValue(event)
yoyo201626 commented 8 months ago

It seems that the component didn’t calculate the position after scroll

// renderless/src/slider/index.ts
// bindMouseDown()
+ api.bindResize()
const currentValue  = api.calculateValue(event)

@Huauauaa I think more critical is this code in packages\renderless\src\slider\index.ts#calculateValue:


(event) => {
let currentValue = 0
if (state.sliderSize == 0) {
  const handleEl = vm.$refs.slider
  state.sliderSize = handleEl['client' + (props.vertical ? 'Height' : 'Width')]
  state.sliderOffset = handleEl.getBoundingClientRect()
}

const offset = state.sliderOffset as DOMRect

if (event.type === 'touchmove' || event.type === 'touchstart' || event.type === 'touchend') {
  if (props.vertical) {
    currentValue = props.max - ((event.touches[0].pageY - offset.top) / state.sliderSize) * state.rangeDiff
  } else {
    currentValue = props.min + ((event.touches[0].pageX - offset.left) / state.sliderSize) * state.rangeDiff
  }
} else {
  if (props.vertical) {
    currentValue = props.max - ((event.pageY - offset.top) / state.sliderSize) * state.rangeDiff
  } else {
    currentValue = props.min + ((event.pageX - offset.left) / state.sliderSize) * state.rangeDiff
  }
}
return currentValue

}


`event.pageY` is the Y coordinate relative to **the entire document**, but `offset.top` which get from `Element.getBoundingClientRect()` is relative to **the Viewport**
yoyo201626 commented 7 months ago

应该选择使用相对 Viewport 的定位来计算

因为当前没有好办法来准确获取某个Element的相对于document的定位。

这对于鼠标事件event来说,只要把event.pageY改成event.clientY就可以。

但是对于当前锚点元素handleEl来说,为了减少回流,我们不能太频繁地使用Element.getBoundingClientRect()来获取相对于ViewPort的定位。这是需要解决的。

Issues-translate-bot commented 7 months ago

Bot detected the issue body's language is not English, translate it automatically.


You should choose to use positioning relative to the Viewport to calculate

Because there is currently no good way to accurately obtain the positioning of an Element relative to the document.

For the mouse event event, just change event.pageY to event.clientY.

But for the current anchor element handleEl, in order to reduce reflow, we cannot use Element.getBoundingClientRect() too frequently to obtain the position relative to the ViewPort. This needs to be addressed.