Open signature in a landscape overlay and fix mobile touch
The inline signature pad on the confirm page was unusable on phones: the canvas had a fixed 480px internal width but shrank via max-width on small screens, so pointer coordinates landed in only the top-left fraction of the drawing buffer. Edge-of-screen strokes also collided with iOS Safari back-swipe. Tapping the new signature trigger now opens a full-screen overlay that rotates to landscape (via 100dvh/100dvw + CSS rotate on portrait phones, plus an opportunistic screen.orientation.lock) so customers get the widest possible signing area, away from the system edge. Also swap the hand-rolled drawing logic for signature_pad, with a ResizeObserver-driven resize that preserves strokes via toData/fromData and scales the canvas to devicePixelRatio. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import SignaturePad, { type SignaturePadHandle } from './SignaturePad';
|
||||
import './SignatureOverlay.css';
|
||||
|
||||
interface SignatureOverlayProps {
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onConfirm: (dataUrl: string) => void;
|
||||
}
|
||||
|
||||
function SignatureOverlay({ open, onCancel, onConfirm }: SignatureOverlayProps) {
|
||||
const padRef = useRef<SignaturePadHandle>(null);
|
||||
const [data, setData] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
||||
const orientation = (screen as Screen & {
|
||||
orientation?: { lock?: (o: string) => Promise<void> };
|
||||
}).orientation;
|
||||
if (orientation?.lock) {
|
||||
orientation.lock('landscape').catch(() => {
|
||||
// ignore; iOS Safari etc. don't permit lock outside fullscreen
|
||||
});
|
||||
}
|
||||
|
||||
const prevOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = 'hidden';
|
||||
return () => {
|
||||
document.body.style.overflow = prevOverflow;
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
const handleClear = () => {
|
||||
padRef.current?.clear();
|
||||
setData('');
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (padRef.current?.isEmpty()) return;
|
||||
const url = padRef.current?.getDataURL() || data;
|
||||
if (!url) return;
|
||||
onConfirm(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="signature-overlay" role="dialog" aria-modal="true">
|
||||
<div className="signature-overlay-stage">
|
||||
<div className="signature-overlay-bar">
|
||||
<Button type="text" onClick={onCancel}>
|
||||
取消
|
||||
</Button>
|
||||
<span className="signature-overlay-title">请在框内签名</span>
|
||||
<Button type="text" onClick={handleClear}>
|
||||
清除
|
||||
</Button>
|
||||
</div>
|
||||
<div className="signature-overlay-pad">
|
||||
<SignaturePad ref={padRef} onChange={setData} />
|
||||
</div>
|
||||
<div className="signature-overlay-actions">
|
||||
<Button size="large" type="primary" block onClick={handleConfirm}>
|
||||
保存签名
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SignatureOverlay;
|
||||
Reference in New Issue
Block a user