Dev3 laser keepupdating - #244
Open
hiroshisuga wants to merge 111 commits into
Open
hiroshisuga wants to merge 111 commits into
hiroshisuga wants to merge 111 commits into
Conversation
To adopt this PR to popup again, you need: - to change -> "const targetDoc = popupWindow?.document || document;" (at two places whare "const targetDoc = document"; is writted) - to add-> "popupWindow" at the ends of two React.useEffect()s which defines "const targetDoc" (same functions above)
Automated tests Summary🚨 Test workflow has failedClick here to check the action test reports (to view the report locally, see the docs) |
Remove <svg class="tl-collaborator__cursor-hint"> that appears typically when the cursor moves near the right or the bottom edge of the viewer's screen. We do not need it when the laser is shown.
This change made the PR portable and easily mergeable to the popup PR.
Previously a pinching gesture is mis-recognized as a long touch, giving rise to the laser selector popup.
「2本目のタッチをtldrawが処理する前にcamera操作を有効化するため、gesture開始の判定には pointerdown を使用する。一方、multi-touch中に canMoveCamera を切り替えるとtldrawのpinch状態と競合するため、gesture終了は touchend で全指が離れた場合のみとし、それまではcamera状態を維持する。」 今回の修正は、もともと「レーザーポインタ使用中のタッチ操作」を通常の Hand tool と同じように tldraw に任せていたことから始まっています。 通常の Hand tool では、タッチスクリーン上で1本指をドラッグするとキャンバスがパンします。しかしレーザー使用中は、1本指はレーザーを動かすために使いたいため、期待する操作は次のようになります。 1本指:レーザーを動かす。キャンバスはパンしない 2本指:通常どおりパン/ピンチズームする 2本指操作が終わった後:残った1本指を新しいレーザー操作として誤認しない このため、tldraw の canMoveCamera をタッチ本数に応じて切り替えることにしました。ただし、ここでいくつか問題がありました。 最初は touchstart を使って、 1本 → canMoveCamera = false 2本 → canMoveCamera = true としていました。しかしこれは、2本目の指が触れたときに canMoveCamera を有効化するタイミングが遅すぎました。 タッチ端末では概念的に、 2本目の pointerdown ↓ tldraw が pointerdown を処理 ↓ touchstart という順序になります。 そのため touchstart で canMoveCamera = true にした時点では、tldraw はすでに2本目のpointerを、camera movementが禁止された状態で処理していました。この中途半端な状態からpinchへ移行することで、pinch開始時の挙動が不安定になったり、camera位置が飛んだりしました。 そこで、gesture開始の判定には pointerdown を使うようにしました。 PointerEvent.isPrimary を使えば、自前で指の本数を数えなくても、 最初の指 pointerdown / isPrimary = true → canMoveCamera = false 2本目 pointerdown / isPrimary = false → canMoveCamera = originalCanMoveCamera と判断できます。 特に2本目ではcapture phaseの pointerdown で先にcamera movementを有効化するため、tldrawが2本目を処理する時点ですでにpinch/pan可能な状態になっています。これによって2本指panとpinch zoomが安定しました。 もう一つの問題は、最初の実装で const activeTouchPointers = new Set(); を使い、pointerdown / pointerup / pointercancel を全部追跡していたことです。 一見正確そうですが、実際のiPad/Androidのmulti-touchでは、pinch中にブラウザやtldraw側のgesture処理によって pointercancel が発生することがあります。その結果、自前の activeTouchPointers とブラウザ/tldrawが認識している実際のgesture状態がずれることがありました。 特に、 2本 → 1本 → activeTouchPointers.size === 1 → canMoveCamera = false とgesture途中で状態を変更すると、tldrawのpinch終了処理と競合しました。その結果、まれに1本指入力が効かなくなったり、laserだけでなくpen toolまで反応しなくなる状態が発生しました。もう一度pinchすると復活することがあったのも、次のmulti-touch gestureによってtldrawの内部入力状態が再構成されていたためと考えられます。 そこで、pointerの終了を一本一本管理することをやめました。 現在の方針は、 最初の pointerdown → canMoveCamera = false 2本目の pointerdown → multiTouchGesture = true → canMoveCamera = originalCanMoveCamera 2本 → 1本 → 何もしない 全指が離れた touchend → gesture終了 → canMoveCameraを元に戻す です。 特に重要なのは 2本から1本になったときに何もしないことです。 一度multi-touch gestureが始まったら、そのgestureはすべての指が画面から離れるまでcamera操作として扱います。途中で canMoveCamera を切り替えないため、tldrawのpinch stateと衝突しなくなりました。 つまり、最終的にはイベントの役割を分けたのがポイントです。 pointerdown → gestureが「1本指か2本指か」を早い段階で判定するために使う touchend → gesture全体が本当に終了したか (touches.length === 0) を判断するために使う この組み合わせにより、 laser使用中だけ1本指panを禁止 通常のHand toolには影響しない 2本指panは正常 pinch zoomも正常 2→1の途中でcamera状態を変更しないため入力が死なない という状態にできました。 さらに、pinch終了時に片方の指だけが少し遅れて離れると、その残った指をレーザー操作として拾ってしまう問題については、multi-touchから1本に戻った後を「まだ前のgestureの一部」として扱い、次の新しいprimary touchが始まるまでlaser位置更新を無視する、という考え方で抑制できます。
IMPORTANT change! 以前は二本指でズームやパンしていて、一本でも指がスライドの外に出た場合、viewerにズームやパン情報が伝わらなかったが、これ一発で修正できた。
Visually remove the laser when presenter move it outside of the slide by two- or one-finger drag on touch devices.
Laser must be shown beyond the slide area.
<Before this change, fit-to-width button broke the laser UI, namely, the disabled one-finger pan> Reason for this change Changing fitToWidth recreates the tldraw subtree because fitToWidth is included in the component key. As a result, both the tldraw editor instance and presentationInnerWrapper are replaced. The touch-handling effect previously read tlEditorRef.current only when the effect was initialized. Its event handlers therefore retained the old editor and were attached to the old wrapper after tldraw was remounted. Updating tlEditorRef.current alone did not fix this because changing a ref does not trigger a React render or rerun an effect. Why this fixes the issue The mounted editor is now also stored in React state and updated from handleTldrawMount. When tldraw creates a new editor, the state update causes the touch-handling effect to run again. The old event listeners are removed, and new listeners are registered using the newly mounted editor and wrapper. Consequently, after toggling “fit to width,” a primary single-finger touch once again sets canMoveCamera to false, while adding a second finger restores camera movement for pan and pinch gestures. The editor itself is not duplicated: the ref and state both point to the same editor instance. The ref provides immediate access to the current editor, while the state makes editor replacement observable to React.
当初は、effect内でtlEditorRef.currentを直接参照していたため、通常の初回使用では正常に動作していた。しかし、fitToWidthを切り替えるとTldrawが再マウントされ、editorが入れ替わってもeffectが再実行されず、一本指パンの無効化が効かなくなっていた。 そこでeditorの入れ替わりをstateで検知するよう変更したが、handleTldrawMountでsetMountedTldrawEditor(editor)を呼び出す処理が抜けていた。この行を追加することで、新しいeditorのマウント後にeffectが再実行され、fitToWidth切り替え後も一本指パンが正しく無効化されるようになった。
…onPublishCursor.tx It will never happen as long as the latest HTML5 client is distributed to every viewer.
…server/presPageCursorStream.go It will not happen either, unless an old HTML5-client is distributed to viewers.
Fires only when the presenter uses mobile devices.
🚨 Automated tests failed |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[Laser pointer implementation]
Motivation
but no laser (actually any other cursors) shown as a presenter although laser visible in the viewer's screen(Aug 2026)
To do and problems
Not working for a presenter on iOS (as a viewer there is no problem)akka-apps does not start properly (killed gracefully).bbb-conf --restart sometimes does not work for that reason. -> this is not the problem of the source code. By apt install --reinstall bbb-apps-akka can recover the problem (or simply, sudo systemctl start bbb-apps-akka) : the point is to execute: ln -s /lib/systemd/system/bbb-apps-akka.service /etc/systemd/system/multi-user.target.wants/bbb-apps-akka.service , and ln -s /lib/systemd/system/bbb-apps-akka.service /etc/systemd/system/bigbluebutton.target.wants/bbb-apps-akka.service , which are not included in the deploy.sh in akka-bbb-apps source code. -> fix(akka-apps): [development] Correctly start bbb-apps-akka by creating symlinks bigbluebutton/bigbluebutton#25372 fixed the problemTo use this PR with the popup PR, you need:
"const targetDoc = popupWindow?.document || document;"(at many places where "const targetDoc = document"; is used)"const targetDoc = getWhiteboardDocument();"-> not necessary anymore after a7de71ato add-> "popupWindow" at the ends of two React.useEffect()s which defines "const targetDoc" (same functions above)(when refactoring on Apr 26, it turned out that they need not to be changed)useCursorin the hooks.js and component.jsx to be the same. This may not be warned by the merging conflict check of Github.