diff --git a/public/images/news/saveclip-app-605404066-17852939781600031-474959415-1774477696991.jpg b/public/images/news/saveclip-app-605404066-17852939781600031-474959415-1774477696991.jpg new file mode 100644 index 0000000..2a5d46f Binary files /dev/null and b/public/images/news/saveclip-app-605404066-17852939781600031-474959415-1774477696991.jpg differ diff --git a/public/images/news/saveclip-app-605404066-17852939781600031-474959415-1774511759934.jpg b/public/images/news/saveclip-app-605404066-17852939781600031-474959415-1774511759934.jpg new file mode 100644 index 0000000..2a5d46f Binary files /dev/null and b/public/images/news/saveclip-app-605404066-17852939781600031-474959415-1774511759934.jpg differ diff --git a/src/app/admin/news/page.tsx b/src/app/admin/news/page.tsx index 66ee6e4..428b819 100644 --- a/src/app/admin/news/page.tsx +++ b/src/app/admin/news/page.tsx @@ -18,17 +18,22 @@ function CropPreview({ image, focalX, focalY, + zoom, onImageChange, onFocalChange, + onZoomChange, }: { image: string; focalX: number; focalY: number; + zoom: number; onImageChange: (path: string) => void; onFocalChange: (x: number, y: number) => void; + onZoomChange: (z: number) => void; }) { const [uploading, setUploading] = useState(false); const [dragging, setDragging] = useState(false); + const dragStartRef = useRef({ x: 0, y: 0, startFocalX: 0, startFocalY: 0 }); const containerRef = useRef(null); async function handleUpload(e: React.ChangeEvent) { @@ -52,39 +57,51 @@ function CropPreview({ } } - function updateFocalFromEvent(clientX: number, clientY: number) { - const el = containerRef.current; - if (!el) return; - const rect = el.getBoundingClientRect(); - const x = Math.max(0, Math.min(100, ((clientX - rect.left) / rect.width) * 100)); - const y = Math.max(0, Math.min(100, ((clientY - rect.top) / rect.height) * 100)); - onFocalChange(Math.round(x), Math.round(y)); - } - function handlePointerDown(e: React.PointerEvent) { e.preventDefault(); (e.target as HTMLElement).setPointerCapture(e.pointerId); setDragging(true); - updateFocalFromEvent(e.clientX, e.clientY); + dragStartRef.current = { + x: e.clientX, + y: e.clientY, + startFocalX: focalX, + startFocalY: focalY, + }; } function handlePointerMove(e: React.PointerEvent) { if (!dragging) return; - updateFocalFromEvent(e.clientX, e.clientY); + const el = containerRef.current; + if (!el) return; + const rect = el.getBoundingClientRect(); + const { x: startX, y: startY, startFocalX, startFocalY } = dragStartRef.current; + // Invert: dragging right moves focal left (image slides right) + const dx = ((e.clientX - startX) / rect.width) * 100; + const dy = ((e.clientY - startY) / rect.height) * 100; + const newX = Math.max(0, Math.min(100, startFocalX - dx)); + const newY = Math.max(0, Math.min(100, startFocalY - dy)); + onFocalChange(Math.round(newX), Math.round(newY)); } function handlePointerUp() { setDragging(false); } + function handleWheel(e: React.WheelEvent) { + e.preventDefault(); + const delta = e.deltaY > 0 ? -0.1 : 0.1; + const newZoom = Math.max(1, Math.min(3, zoom + delta)); + onZoomChange(Math.round(newZoom * 10) / 10); + } + return (
{image ? (
- {/* Crop area — drag to reposition */} + {/* Crop area — drag image to reposition */}
Превью
- {/* Actions */} -
+ {/* Zoom slider + actions */} +
+
+ + onZoomChange(parseFloat(e.target.value))} + className="flex-1 h-1 accent-[#c9a96e] cursor-pointer" + /> + + + {zoom > 1 && ( + + )} +
)} diff --git a/src/components/ui/NewsModal.tsx b/src/components/ui/NewsModal.tsx index e882f00..fda169f 100644 --- a/src/components/ui/NewsModal.tsx +++ b/src/components/ui/NewsModal.tsx @@ -82,7 +82,10 @@ export function NewsModal({ item, onClose }: NewsModalProps) { fill sizes="(min-width: 768px) 672px, 100vw" className="object-cover" - style={{ objectPosition: `${item.imageFocalX ?? 50}% ${item.imageFocalY ?? 50}%` }} + style={{ + objectPosition: `${item.imageFocalX ?? 50}% ${item.imageFocalY ?? 50}%`, + transform: `scale(${item.imageZoom ?? 1})`, + }} />
diff --git a/src/types/content.ts b/src/types/content.ts index c53b877..5129b8a 100644 --- a/src/types/content.ts +++ b/src/types/content.ts @@ -86,6 +86,7 @@ export interface NewsItem { image?: string; imageFocalX?: number; // 0-100, default 50 imageFocalY?: number; // 0-100, default 50 + imageZoom?: number; // 1-3, default 1 link?: string; }