スマホで動くお絵かきツールみたいなのを書いている時に、Canvasの上をスワイプでなぞった箇所に線を引くようにしたら、線を引きながらページがスクロールしてしまう現象にはまりました。
これでは使い物にならないので、Canvasの上でスワイプしたときはスクロールさせないようにしたい。という時はCSSでtouch-action: none;
を指定してあげると良いようです。最初試しもせずに、これを設定するとonTouchMoveイベントも発生しなくなるのでは…と思って無駄に遠回りしてしまいました。
せっかくなので以下遠回りした記録。onTouchMoveでpreventDefault()を呼べばスクロールを無効化できるよね、と思って、当初以下のように書きました。React使ってます。
function App() { const handleTouchMove = (event: React.TouchEvent) => { event.preventDefault(); // これは効かない // => [Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. } return ( <div className="App"> <div className="scroll-off" onTouchMove={handleTouchMove}> {/* この要素の中だけスクロールを無効にしたい */} </div> </div> ); }
しかし、コメントにあるとおりこれは動作しません。
Reactの要素にはonTouchMoveCaptureというイベントも別にありますが、これを使っても挙動は変わらないので、どうしてもpreventDefaultを使いたい場合は以下のようにする必要がありそうです。
function App() { const divRef = React.useRef<HTMLDivElement>(null); React.useEffect(() => { const elem = divRef.current! elem.addEventListener('touchmove', (e: any) => handleTouchMove(e), { passive: false }); return () => elem.removeEventListener('touchmove', (e: any) => handleTouchMove(e)); }, []); const handleTouchMove = (event: React.TouchEvent) => { event.preventDefault(); } return ( <div className="App"> <div className="scroll-off" ref={divRef}> {/* この要素の中だけスクロールを無効にしたい */} </div> </div> ); }
さすがにこれはいまいちですよねぇ。というわけで最初に戻って
function App() { const handleTouchMove = (event: React.TouchEvent) => { // ... } return ( <div className="App"> <div className="scroll-off" style={{ touchAction: 'none' }} onTouchMove={handleTouchMove}> {/* この要素の中だけスクロールを無効にしたい */} </div> </div> ); }
でいいかなということに。