Persekitaran pengendalian tutorial ini: sistem Windows 10, bertindak balas versi 18.0.0, komputer Dell G3.
Bagaimana untuk mencapai penskalaan dalam tindak balas?
Penskalaan dan panning imej bertindak balas (kedudukan, pelaksanaan transformasi)
Banyak halaman web akan melampirkan beberapa gambar untuk menambah penerangan salinan, contohnya apabila bercakap tentang alamat , peta akan dilampirkan di sebelahnya dan alamat akan ditanda pada peta. Jika gambar yang dilampirkan sangat kecil dan sukar untuk melihat maklumat khusus alamat dengan jelas, sesetengah pengurus produk akan mereka bentuk fungsi untuk menyorot, mengezum masuk dan keluar dari gambar. Artikel ini akan melaksanakan fungsi di atas satu demi satu.
Tanpa berlengah lagi, berikut ialah renderingnya:
Tiga perkara fungsi utama:
- Kuli gambar
- Zum gambar
- Label stesen
Kuli gambar
Kuli gambar boleh dilaksanakan dengan memantau ketiga-tiga peristiwa ini: onMouseDown、onMouseMove、onMouseUp
. Acara onMouseDown
merekodkan kedudukan koordinat setiap klik tetikus; acara onMouseMove
mengira jarak setiap terjemahan Jarak ini ditambah jarak antara gambar dan elemen induk sebelum menyeret adalah sama dengan jarak selepas menyeret. Jarak gambar relatif kepada elemen induk; apabila acara onMouseUp
dicetuskan, log keluar atau menghalang pelaksanaan acara onMouseDown、onMouseMove
untuk menghalang gambar daripada menyorot selagi tetikus dipindahkan ke dalamnya.
Tiga peristiwa ini perlu menghalang kelakuan lalai penyemak imbas, jika tidak, imej akan dibuka secara automatik apabila bergerak.
const WIDTH = 1200;const HEIGHT = 900;const DynamicStyle= () => { const imgRef = React.createRef<HTMLImageElement>(); /** 图片样式 */ const [imgStyle, setImgStyle] = useState<React.CSSProperties>({}); /** 记录鼠标是否按下 */ const [mouseDowmFlag, setMouseDowmFlag] = useState(false); /** 记录鼠标按下的坐标 */ const [mouseDowmPos, setMouseDowmPos] = useState<{x: number, y: number}>({x: 0, y: 0}) /** 鼠标可视区域时,重置鼠标按下的布尔值为false */ useEffect(() => { document.onmouseover = () => { if (mouseDowmFlag) { setMouseDowmFlag(false); } }; return () => { document.onmouseover = null; }; }, [mouseDowmFlag]) /** 平移 */ const handleMouseDown = (event: React.MouseEvent<HTMLImageElement>) => { const { clientX, clientY } = event; event.stopPropagation(); event.preventDefault(); // 阻止浏览器默认行为,拖动会打开图片 setMouseDowmFlag(true); // 控制只有在鼠标按下后才会执行mousemove setMouseDowmPos({ x: clientX, y: clientY, }); }; const handleMouseMove = (event: React.MouseEvent<HTMLImageElement>) => { event.stopPropagation(); event.preventDefault(); const { clientX, clientY } = event; const diffX = clientX - mouseDowmPos.x; const diffY = clientY - mouseDowmPos.y; if (!mouseDowmFlag || (diffX === 0 && diffY === 0)) return; const { offsetLeft, offsetTop } = imgRef.current as HTMLImageElement; const offsetX = parseInt(`${diffX + offsetLeft}`, 10); const offsetY = parseInt(`${diffY + offsetTop}`, 10); setMouseDowmPos({ x: clientX, y: clientY, }); setImgStyle({ ...imgStyle, left: offsetX, top: offsetY, }); }; const handleMouseUp = (event: React.MouseEvent<HTMLImageElement>) => { event.stopPropagation(); event.preventDefault(); setMouseDowmFlag(false); }; return ( <div className={styles.imgArea}> <img src={mapImg} alt='part' ref={imgRef} height={HEIGHT} style={imgStyle} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} > </img> </div> ) }
Zum gambar
Zum gambar boleh mendengar acara onWheel
Objek acara event
mempunyai atribut deltaY
yang merekodkan skrol roda apabila menatal ke atas deltaY, <code>deltaY>0
apabila menatal ke bawah. Setiap skrol menukar perkadaran penskalaannya dan menukar gaya transform
kepada skala secara berkadar.
const WIDTH = 1200;const HEIGHT = 900;const SCALE = 0.2;const DynamicStyle= () => { const imgRef = React.createRef<HTMLImageElement>(); /** 初始化缩放比例,默认为1 */ const [rate, setRate] = useState(1); /** 图片样式 */ const [imgStyle, setImgStyle] = useState<React.CSSProperties>({}); /** 记录鼠标是否按下 */ const [mouseDowmFlag, setMouseDowmFlag] = useState(false); /** 记录鼠标按下的坐标 */ const [mouseDowmPos, setMouseDowmPos] = useState<{x: number, y: number}>({x: 0, y: 0}) /** 图片现在大小 */ const [initial, setInitial] = useState<{width: number, height: number}>({width: WIDTH, height: HEIGHT}); useEffect(() => { const { naturalWidth, naturalHeight, width, height } = imgRef.current as HTMLImageElement; setInitial({ width, height }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // console.log(natural, initial) useEffect(() => { document.onmouseover = () => { if (mouseDowmFlag) { setMouseDowmFlag(false); } }; return () => { document.onmouseover = null; }; }, [mouseDowmFlag]) /** 缩放 */ const handleWheelImage = (event: React.WheelEvent<HTMLImageElement>) => { // 向上为负,向下为正 const bigger = event.deltaY > 0 ? -1 : 1; // transform偏移量 const transformX = -initial.width / 2; const transformY = -initial.height / 2; if (bigger > 0 && rate < 2) { const enlargeRate = rate + SCALE; setImgStyle({ ...imgStyle, transform: `matrix(${enlargeRate}, 0, 0, ${enlargeRate}, ${transformX}, ${transformY})`, // 默认以图片中心为原点进行缩放 }); setRate(enlargeRate); } else if (bigger < 0 && rate > 1) { const shrinkRate = rate - SCALE; setImgStyle({ ...imgStyle, transform: `matrix(${shrinkRate}, 0, 0, ${shrinkRate}, ${transformX}, ${transformY})`, }); setRate(shrinkRate); } } /** 平移 */ const handleMouseDown = (event: React.MouseEvent<HTMLImageElement>) => { const { clientX, clientY } = event; event.stopPropagation(); event.preventDefault(); // 阻止浏览器默认行为,拖动会打开图片 setMouseDowmFlag(true); // 控制只有在鼠标按下后才会执行mousemove setMouseDowmPos({ x: clientX, y: clientY, }); }; const handleMouseMove = (event: React.MouseEvent<HTMLImageElement>) => { event.stopPropagation(); event.preventDefault(); const { clientX, clientY } = event; const diffX = clientX - mouseDowmPos.x; const diffY = clientY - mouseDowmPos.y; if (!mouseDowmFlag || (diffX === 0 && diffY === 0)) return; const { offsetLeft, offsetTop } = imgRef.current as HTMLImageElement; const offsetX = parseInt(`${diffX + offsetLeft}`, 10); const offsetY = parseInt(`${diffY + offsetTop}`, 10); setMouseDowmPos({ x: clientX, y: clientY, }); setImgStyle({ ...imgStyle, left: offsetX, top: offsetY, }); }; const handleMouseUp = (event: React.MouseEvent<HTMLImageElement>) => { event.stopPropagation(); event.preventDefault(); setMouseDowmFlag(false); }; return ( <div className={styles.imgArea}> <img src={mapImg} alt='part' height={HEIGHT} style={imgStyle} ref={imgRef} onWheel={handleWheelImage} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} > </img> </div> ) }
.imgArea { position: relative; width: 1200px; height: 900px; margin: auto; border: 1px solid #da2727; overflow: hidden; & > img { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); cursor: move; } }
Jika transformOrigin
tidak ditetapkan, lalai adalah untuk menskalakan relatif kepada tengah imej, tetapi pada mulanya untuk membuat imej mendatar dan menegak dalam kawasan yang boleh dilihat Di tengah, transform: translate(-50%, -50%);
digunakan, jadi untuk menskala relatif kepada titik tengah gambar, parameter ke-5 dan ke-6 matrix
perlu ditetapkan untuk membetulkan transformOrigin
, transform: matrix(${shrinkRate}, 0, 0, ${shrinkRate}, ${transformX}, ${transformY})
label stesen
Mula-mula, tentukan pemalar untuk mewakili koordinat ikon ini adalah relatif kepada penjuru kiri sebelah atas imej asal.
const imgInfo = { lableLeft: "1900", lableTop: "2000",}
Di sini, terangkan konsep imej asal:
Lihat mana-mana elemen imej dalam talian, seperti yang di atas. 1200 x 900 ialah saiz imej yang dinyatakan pada halaman, tetapi imej itu juga mempunyai saiz sebenar 4535 x 3402.
Sebelum mengira koordinat awal ikon tanpa terjemahan dan penskalaan, anda perlu mengira nisbah penskalaan imej (bukan di atas rate
):
/** 图片原始大小,默认设置为1是防止计算图片原始大小与初始大小比例出现无穷大 */const [natural, setNatural] = useState<{width: number, height: number}>({width: 1, height: 1});/** 图片现在大小 */const [initial, setInitial] = useState<{width: number, height: number}>({width: WIDTH, height: HEIGHT});useEffect(() => { const { naturalWidth, naturalHeight, width, height } = imgRef.current as HTMLImageElement; setNatural({ width: naturalWidth, height: naturalHeight }); setInitial({ width, height }); // eslint-disable-next-line react-hooks/exhaustive-deps}, []) // 初始图片缩放比例(图片有原始的图片大小)const imgScaleRateX = initial.width / natural.width;const imgScaleRateY = initial.height / natural.height;
Koordinat awal daripada ikon boleh dikira Keluar:
const labelLeft = parseInt(`${imgInfo.lableLeft}`, 10) * imgScaleRateX;const labelTop = parseInt(`${imgInfo.lableTop}`, 10) * imgScaleRateY;
Apabila gambar diterjemahkan, ikon juga perlu diterjemahkan Ini ialah pengiraan koordinat:
// 图标相对父元素坐标 = 图标位置坐标 + 图片坐标const labelLeft = parseInt(`${imgInfo.lableLeft}`, 10) * imgScaleRateX + Number(imgStyle.left || WIDTH / 2); const labelTop = parseInt(`${imgInfo.lableTop}`, 10) * imgScaleRateY + Number(imgStyle.top || HEIGHT / 2);
<.>
Apabila gambar dizum , ikon perlu berskala bersama imej. Jika tidak ditetapkan untuk ikon, ia akan diskalakan relatif kepada tengah ikon secara lalai. Untuk memastikan ikon berskala dengan imej, asal rujukan penskalaan imej dan ikon mestilah sama dan transformOrigin
ikon hendaklah ditetapkan kepada jarak berbanding dengan asal imej. transformOrigin
const labelTransformOrigin = () => { return `${initial.width / 2 - Number(imgInfo.lableLeft) * imgScaleRateX}px ${ initial.height / 2 - Number(imgInfo.lableTop) * imgScaleRateY }px`; }Contoh kod keseluruhan:
const imgInfo = { lableLeft: "1900", lableTop: "2000", } const WIDTH = 1200; const HEIGHT = 900; const SCALE = 0.2; const DynamicStyle= () => { const imgRef = React.createRef<HTMLImageElement>(); /** 初始化缩放比例,默认为1 */ const [rate, setRate] = useState(1); /** 图片样式 */ const [imgStyle, setImgStyle] = useState<React.CSSProperties>({}); /** 记录鼠标是否按下 */ const [mouseDowmFlag, setMouseDowmFlag] = useState(false); /** 记录鼠标按下的坐标 */ const [mouseDowmPos, setMouseDowmPos] = useState<{x: number, y: number}>({x: 0, y: 0}) /** 图片原始大小,默认设置为1是防止计算图片原始大小与初始大小比例出现无穷大 */ const [natural, setNatural] = useState<{width: number, height: number}>({width: 1, height: 1}); /** 图片现在大小 */ const [initial, setInitial] = useState<{width: number, height: number}>({width: WIDTH, height: HEIGHT}); useEffect(() => { const { naturalWidth, naturalHeight, width, height } = imgRef.current as HTMLImageElement; setNatural({ width: naturalWidth, height: naturalHeight }); setInitial({ width, height }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []) useEffect(() => { document.onmouseover = () => { if (mouseDowmFlag) { setMouseDowmFlag(false); } }; return () => { document.onmouseover = null; }; }, [mouseDowmFlag]) /** 缩放 */ const handleWheelImage = (event: React.WheelEvent<HTMLImageElement>) => { // 向上为负,向下为正 const bigger = event.deltaY > 0 ? -1 : 1; // transform偏移量 const transformX = -initial.width / 2; const transformY = -initial.height / 2; if (bigger > 0 && rate < 2) { const enlargeRate = rate + SCALE; setImgStyle({ ...imgStyle, transform: `matrix(${enlargeRate}, 0, 0, ${enlargeRate}, ${transformX}, ${transformY})`, // 默认以图片中心为原点进行缩放 }); setRate(enlargeRate); } else if (bigger < 0 && rate > 1) { const shrinkRate = rate - SCALE; setImgStyle({ ...imgStyle, transform: `matrix(${shrinkRate}, 0, 0, ${shrinkRate}, ${transformX}, ${transformY})`, }); setRate(shrinkRate); } } /** 平移 */ const handleMouseDown = (event: React.MouseEvent<HTMLImageElement>) => { const { clientX, clientY } = event; event.stopPropagation(); event.preventDefault(); // 阻止浏览器默认行为,拖动会打开图片 setMouseDowmFlag(true); // 控制只有在鼠标按下后才会执行mousemove setMouseDowmPos({ x: clientX, y: clientY, }); }; const handleMouseMove = (event: React.MouseEvent<HTMLImageElement>) => { event.stopPropagation(); event.preventDefault(); const { clientX, clientY } = event; const diffX = clientX - mouseDowmPos.x; const diffY = clientY - mouseDowmPos.y; if (!mouseDowmFlag || (diffX === 0 && diffY === 0)) return; const { offsetLeft, offsetTop } = imgRef.current as HTMLImageElement; const offsetX = parseInt(`${diffX + offsetLeft}`, 10); const offsetY = parseInt(`${diffY + offsetTop}`, 10); setMouseDowmPos({ x: clientX, y: clientY, }); setImgStyle({ ...imgStyle, left: offsetX, top: offsetY, }); }; const handleMouseUp = (event: React.MouseEvent<HTMLImageElement>) => { event.stopPropagation(); event.preventDefault(); setMouseDowmFlag(false); }; // 初始图片缩放比例(图片有原始的图片大小) const imgScaleRateX = initial.width / natural.width; const imgScaleRateY = initial.height / natural.height; const labelTransformOrigin = () => { return `${initial.width / 2 - Number(imgInfo.lableLeft) * imgScaleRateX}px ${ initial.height / 2 - Number(imgInfo.lableTop) * imgScaleRateY }px`; } /** 图标位置计算 */ const labelStyle = (): React.CSSProperties => { const transformX = -initial.width / 2; const transformY = -initial.height / 2; // 图标相对父元素坐标 = 图标初始位置坐标 + 平移量 const labelLeft = parseInt(`${imgInfo.lableLeft}`, 10) * imgScaleRateX + Number(imgStyle.left || WIDTH / 2); const labelTop = parseInt(`${imgInfo.lableTop}`, 10) * imgScaleRateY + Number(imgStyle.top || HEIGHT / 2); return { left: labelLeft, top: labelTop, transformOrigin: labelTransformOrigin(), transform: `matrix(${rate}, 0, 0, ${rate}, ${transformX}, ${transformY})`, } } return ( <div className={styles.imgArea}> <img src={mapImg} alt='part' height={HEIGHT} style={imgStyle} ref={imgRef} onWheel={handleWheelImage} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} > </img> <span className={styles.label} style={labelStyle()}></span> </div> ) }Pembelajaran yang disyorkan: "
tutorial video bertindak balas"