The way I solved this in my own (not finished) web-based spreadsheet app is to have a scrollable div positioned over the canvas and to size the transparent div contents to the width/height of the sheet. Then I register scroll event handlers and redraw the canvas appropriately when the scroll changes. I think this is how Google sheets works, although it's a bit difficult to tell for sure.
It behaves exactly how you would expect. It even properly handles clicking and dragging to the edge of the screen, which I did not expect prior to implementing it.
It behaves exactly how you would expect. It even properly handles clicking and dragging to the edge of the screen, which I did not expect prior to implementing it.