You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pointermove事件触摸设备失效修复及下拉刷新禁用方案咨询

修复触摸拖动光球问题 + 禁用下拉刷新方案

Hey, let's fix those touch interaction issues you're seeing! Here's what's going on and how to resolve it:

问题根源

On touch devices, browsers prioritize default gestures like scrolling or pull-to-refresh over custom pointer events. When you start dragging the light, the browser takes over and stops sending pointermove events to your code. Also, we weren't properly handling the full touch interaction lifecycle (like pointercancel which fires if the browser interrupts your touch session).

解决方案步骤

1. 让光球持续跟随触摸拖动

  • Track when we're actively dragging the light (only when clicking/tapping outside the buttons)
  • Block browser default behaviors during drag to keep pointermove firing consistently
  • Clean up the drag state when the user lifts their finger or the browser interrupts the touch

2. Disable pull-to-refresh

  • We'll block the default touchmove behavior only when we're dragging the light, so button clicks still work normally

完整修改后的代码

HTML (Slim)

/! Made with http://github.com/trusktr/infamous
script src="https://cdn.rawgit.com/trusktr/e37dbc24c51b9d3e2f9e508e75cf8f99/raw/2a3fee4ee506a05cc4ac509f592f0c3af1ddfed4/infamous-mixed-mode-3.js"
script src="https://unpkg.com/tween.js@16.6.0/src/Tween.js"
i-scene experimental-webgl="true" id="scene" TODO-perspective="800" backgroundColor="0 0 0" backgroundOpacity="0" style="perspective: 800px" shadowmap-type="pcfsoft"
i-ambient-light color="#404040" intensity="1"
i-dom-plane id="bg" sizeMode="proportional proportional" size="1 1 0"
i-node id="button-container" position="0 0 6" size="600 31 0" align="0.5 0.5 0" mountPoint="0.5 0.5 0"
- for n in (0..4)
  i-dom-plane sizeMode="literal proportional" size="100 1 0" align="#{n*0.25} 0 0" mountPoint="#{n*0.25} 0 0"
    button button #{n+1}
i-point-light id="light" color="white" position="300 300 120" size="0 0 0" cast-shadow="true" intensity="1"
  i-mesh has="sphere-geometry basic-material" size="10 10 10" color="white" receive-shadow="false" cast-shadow="false" style="pointer-events: none"

CSS (Stylus)

body, html
  width 100%
  height 100%
  margin 0
  padding 0
  font-family sans-serif
i-node
  text-align center
#bg
  background #62B997
button
  width 100%
  height 100%
  white-space nowrap
  border-radius 0px
  border 1px solid #534334
  background lighten(#FB752C, 20%)
  color darken(#534334, 10%)
  outline none // remove those darn ugly browser-specific outlines
  &:focus, &:hover
    background #FB752C
    color darken(#534334, 20%)

JavaScript

infamous.html.useDefaultNames()
const Motor = infamous.core.Motor
light.threeObject3d.shadow.radius = 3
light.threeObject3d.distance = 20000
light.threeObject3d.shadow.bias = 0.00001

// Track if we're actively dragging the light
let isDraggingLight = false
let pressedButton, downTween, upTween

// Handle start of interaction
document.addEventListener('pointerdown', e => {
  // Start dragging if we're not clicking a button
  if (!is(e.target, 'button') && !e.target.closest('button')) {
    isDraggingLight = true
    e.preventDefault() // Block browser defaults to keep pointermove firing
  }

  // Original button press logic
  if (is(e.target, 'button')) {
    pressedButton = e.target
    upTween?.stop()
    upTween = null

    downTween = new TWEEN.Tween(e.target.parentNode.position)
      .to({ z: -6 }, 75)
      .start()
      .onComplete(() => downTween = null)

    Motor.addRenderTask(time => {
      if (!downTween) return false
      downTween.update(time)
    })
  }
})

// Update light position only when dragging
document.addEventListener('pointermove', e => {
  if (isDraggingLight) {
    e.preventDefault()
    light.position.x = e.clientX
    light.position.y = e.clientY
  }
})

// End dragging when user lifts finger or browser interrupts
document.addEventListener('pointerup', () => {
  isDraggingLight = false
})

document.addEventListener('pointercancel', () => {
  isDraggingLight = false
})

// Original button release logic
document.addEventListener('pointerup', e => {
  if (!pressedButton) return

  downTween?.stop()
  downTween = null

  upTween = new TWEEN.Tween(pressedButton.parentNode.position)
    .to({ z: 0 }, 75)
    .start()
    .onComplete(() => upTween = null)

  Motor.addRenderTask(time => {
    if (!upTween) return false
    upTween.update(time)
  })

  pressedButton = null
})

// Disable pull-to-refresh by blocking touchmove during drag
document.addEventListener('touchmove', e => {
  if (isDraggingLight) {
    e.preventDefault()
  }
}, { passive: false }) // Required to allow preventDefault on touchmove

// Temporary opacity fix (original code)
setTimeout(() => {
  Array.from(document.querySelectorAll('i-dom-plane')).forEach(n => {
    n.threeObject3d.material.opacity = 0.3
  })
  scene._needsToBeRendered()
}, 0)

// Helper function (original code)
function is(el, selector) {
  return [].includes.call(document.querySelectorAll(selector), el)
}

关键修改说明

  1. Drag State Tracking: Added isDraggingLight to only process pointermove when we're actually dragging the light (not clicking buttons).
  2. Prevent Default Behaviors: Called e.preventDefault() in pointerdown (drag start) and touchmove (during drag) to stop the browser from hijacking the event. We set passive: false on the touchmove listener because browsers default to passive for performance, which blocks preventDefault.
  3. Handle pointercancel: This event fires if the browser interrupts your touch (like if you switch apps or use a system gesture), so we reset the drag state here to avoid broken interactions.
  4. Cleaned Up Button Logic: Used optional chaining (?.) to make the tween cleanup cleaner, and consolidated variable declarations for better scoping.

内容的提问来源于stack exchange,提问作者trusktr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:55