עם ניסיון של 5+ שנים בפיתוח מסחר אלקטרוני ו-50+ פרויקטי גלריות מוצלחים, אנו מספקים גלריות מוצר עם שיעורי המרה גבוהים. משתמשים עוזבים עמוד אם הם לא יכולים לבחון פרטי מוצר. עיכוב של שנייה אחת בטעינת התמונה הראשונה מפחית את ההמרה ב-20% — כך אישרו מחקרים. גלריית תמונות היא לא רק אוסף תמונות אלא רכיב הנדסי מורכב: ניהול קבצים גדולים, זום ללא אובדן חדות, התאמה למסכי מגע ואופטימיזציה של Core Web Vitals. אנו מפתחים פתרונות כאלה למסחר אלקטרוני, תוך שימוש ב-React 18, Next.js 14, Vue 3 או HTML/CSS/JS ונילה. לחברה שלנו ניסיון של 5+ שנים והיא השלימה 50+ פרויקטי גלריות לחנויות מסחר אלקטרוני. צינור עיבוד התמונות שלנו מפחית את עלות אספקת התוכן ב-40% (חיסכון של $500 לחודש בממוצע) ומגביר את מהירות הטעינה. מפתחים מוסמכים מבטיחים עמידה ב-Core Web Vitals. אנו מפתחים גלריות תמונות מוצר עם שיעורי המרה גבוהים עם זום בהעברת עכבר, צביטה לזום ו-lightbox, כולם מותאמים ל-Core Web Vitals. קבלו ייעוץ על הגלריה שלכם על ידי יצירת קשר איתנו.
מדוע זום משפיע על המרה
משתמשים מצפים לראות פרטים: בד, תפירה, מרקם. ללא זום, הם עוברים למתחרים. לפי Baymard Institute, תמונות באיכות גבוהה מגבירות את ההמרה ב-30%. אנו מיישמים שלושה סוגי זום: זום בהעברת עכבר לשולחן העבודה (מציג אזור מוגדל בעת ריחוף), צביטה לזום לנייד (מחוות שתי אצבעות) ו-lightbox לתצוגת מסך מלא. כל מנגנון מותאם למכשיר ואינו מתנגש עם הניווט.
הכנת תמונות לביצועים מקסימליים
החנות צריכה לאחסן את התמונה ברזולוציה מקסימלית (2000–4000px בצד הארוך) ולספק את הגודל המתאים להקשר. הגשת קובץ 5MB מקורי בכל עמוד היא טעות חמורה. צינור העיבוד בעת העלאת תמונה חדשה:
Загрузка оригинала → S3 (originals/, приватный)
↓
Worker (libvips)
Генерация вариантов:
- thumbnail: 100×100, JPEG 80%, для тумбнейлов
- small: 400×400, JPEG 85%, для списка товаров
- medium: 800×800, JPEG 90%, для основного слота галереи
- large: 1600×1600, JPEG 95%, для зума
- webp: каждый вариант в WebP (обычно 30-50% меньше JPEG)
↓
CDN (публичный бакет или Cloudflare Images)libvips מהיר פי 4–8 מ-ImageMagick וצורך פחות זיכרון משמעותית בעת עיבוד תמונות גדולות. עבור PHP — Загрузка оригинала → S3 (originals/, приватный) ↓ Worker (libvips) Генерация вариантов: - thumbnail: 100×100, JPEG 80%, для тумбнейлов - small: 400×400, JPEG 85%, для списка товаров - medium: 800×800, JPEG 90%, для основного слота галереи - large: 1600×1600, JPEG 95%, для зума - webp: каждый вариант в WebP (обычно 30-50% меньше JPEG) ↓ CDN (публичный бакет или Cloudflare Images) עם מנהל התקן libvips, עבור Node.js — intervention/image.
sharp מודרני עם WebP:
<picture>
<source srcset="product-800.webp" type="image/webp">
<img src="product-800.jpg" alt="галерея изображений товара — Nike Air Max 90" width="800" height="800" loading="lazy">
</picture>אנו ממליצים להשתמש ב-WebP עבור כל העותקים — זה מפחית תעבורה ב-30–50%.
מבנה רכיב הגלריה
פריסה טיפוסית של כרטיס מוצר: חריץ ראשי (תמונה גדולה) + רצועת תמונות ממוזערות מתחת או משמאל. בשולחן העבודה — רצועה אופקית או אנכית; בנייד — החלקה על החריץ הראשי.
function ProductGallery({ images, activeVariantImages }: Props) {
const [activeIndex, setActiveIndex] = useState(0);
const [isZoomed, setIsZoomed] = useState(false);
// При смене варианта — сброс на первое изображение варианта
useEffect(() => {
setActiveIndex(0);
}, [activeVariantImages]);
const allImages = [
...activeVariantImages,
...images.filter(
(img) => !activeVariantImages.find((v) => v.id === img.id)
),
];
return (
<div className="gallery">
<MainSlot
image={allImages[activeIndex]}
onZoom={() => setIsZoomed(true)}
isZoomed={isZoomed}
/>
<Thumbnails
images={allImages}
activeIndex={activeIndex}
onSelect={setActiveIndex}
/>
{isZoomed && (
<LightboxOverlay
images={allImages}
startIndex={activeIndex}
onClose={() => setIsZoomed(false)}
/>
)}
</div>
);
} יישום זום
זום בהעברת עכבר
דפוס קלאסי לשולחן העבודה: בעת ריחוף מעל התמונה, אזור מוגדל מופיע ליד (או מעל) הסמן. טכניקת היישום:
function useHoverZoom(containerRef: RefObject<HTMLDivElement>, scale = 2.5) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [isHovering, setIsHovering] = useState(false);
const handleMouseMove = (e: MouseEvent) => {
const rect = containerRef.current!.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
setPosition({ x, y });
};
return {
position,
isHovering,
handlers: {
onMouseMove: handleMouseMove,
...
}
};
}הצגת זום באמצעות CSS <picture> עם <picture> <source srcset="product-800.webp" type="image/webp"> <img src="product-800.jpg" alt="галерея изображений товара — Nike Air Max 90" width="800" height="800" loading="lazy"> </picture> בנקודת הסמן. תמונת הזום צריכה להיות גדולה פי 2–3 מהמיכל — אחרת הזום יהיה מטושטש.
| סוג זום | מכשירים | אינטראקציה | גודל תמונה לזום |
|---|---|---|---|
| זום בהעברת עכבר | שולחן עבודה | ריחוף עכבר | 1600×1600px (קנה מידה 2.5) |
| צביטה לזום | נייד/טאבלט | שתי אצבעות | 2000×2000px+ (מקסימום 4x) |
| Lightbox | הכל | לחיצה / כפתור | 2000×2000px+ (מקורי) |
צביטה לזום לנייד
במסכי מגע, זום בהעברת עכבר לא עובד. יש צורך בצביטה לזום ו/או הקשה כפולה לזום. React-zoom-pan-pinch היא ספרייה לכך. היא תומכת בצביטה, הקשה כפולה, גלגל זום ופאן:
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
<TransformWrapper
minScale={1}
maxScale={4}
doubleClick={{ mode: 'zoomIn' }}
>
<TransformComponent>
<img src={largeImageUrl} alt={alt} />
</TransformComponent>
</TransformWrapper>חשוב: כאשר הזום פעיל, החלקה אופקית לשינוי תמונות חייבת להיות מושבתת — אחרת יתרחשו התנגשויות מחוות. מצב מעבר: בעוד function ProductGallery({ images, activeVariantImages }: Props) { const [activeIndex, setActiveIndex] = useState(0); const [isZoomed, setIsZoomed] = useState(false); // При смене варианта — сброс на первое изображение варианта useEffect(() => { setActiveIndex(0); }, [activeVariantImages]); const allImages = [...activeVariantImages, ...images.filter( img => !activeVariantImages.find(v => v.id === img.id) )]; return ( <div className="gallery"> <MainSlot image={allImages[activeIndex]} onZoom={() => setIsZoomed(true)} isZoomed={isZoomed} /> <Thumbnails images={allImages} activeIndex={activeIndex} onSelect={setActiveIndex} /> {isZoomed && ( <LightboxOverlay images={allImages} startIndex={activeIndex} onClose={() => setIsZoomed(false)} /> )} </div> ); } — רק פאן; כאשר function useHoverZoom(containerRef: RefObject<HTMLDivElement>, scale = 2.5) { const [position, setPosition] = useState({ x: 0, y: 0 }); const [isHovering, setIsHovering] = useState(false); const handleMouseMove = (e: MouseEvent) => { const rect = containerRef.current!.getBoundingClientRect(); const x = ((e.clientX - rect.left) / rect.width) * 100; const y = ((e.clientY - rect.top) / rect.height) * 100; setPosition({ x, y }); }; return { position, isHovering, handlers: { onMouseMove: handleMouseMove, ... } }; } — אפשר החלקה.
Lightbox / תצוגת מסך מלא
בלחיצה על התמונה (שולחן עבודה) או כפתור "מסך מלא" — פתח מודאלי/lightbox המציג את התמונה במסך מלא. דרישות:
- סגירה עם Escape ולחיצה מחוץ
- ניווט עם מקשי חצים (שמאל/ימין)
- החלקה בנייד
- סרגל תמונות ממוזערות בתחתית
- ה-URL לא משתנה (או hash:
transform: scale())
עבור lightbox אנו משתמשים ב-PhotoSwipe (5kb gzip, תמיכה מצוינת בנייד). PhotoSwipe עובד באופן טבעי עם אירועי מגע ויש לו טעינה פרוגרסיבית (מציג תמונה ממוזערת בזמן שהתמונה הגדולה נטענת).
אופטימיזציית טעינה ותוכן
טעינת תמונות רציפה
אל תטען את כל תמונות הגלריה בבת אחת — זה מאט את LCP. אסטרטגיה:
- תמונה ראשונה —
transform-origin,import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch'; <TransformWrapper minScale={1} maxScale={4} doubleClick={{ mode: 'zoomIn' }} > <TransformComponent> <img src={largeImageUrl} alt={alt} /> </TransformComponent> </TransformWrapper>, טעינה מוקדמת ב-scale > 1 - תמונות ממוזערות — קבצים קטנים, טען את כולן בבת אחת (הן זעירות)
- תמונות גדולות נוספות —
scale === 1או טען רק בעת מעבר לתמונה הממוזערת הזו
בעת ניווט לתמונה N — טען מראש את N+1 ו-N-1 (טעינה מוקדמת של שכנים).
תמיכה בווידאו בגלריה
חנויות רבות מוסיפות ביקורות וידאו ישירות לגלריה — בין התמונות. התמונה הממוזערת של הווידאו היא פריים קפוא עם אייקון הפעלה מעל. בעת בחירה, הווידאו נטען באופן עצלני (#image-3).
פורמטים: MP4/H.264 (תאימות מקסימלית) + WebM/VP9 (גודל קטן יותר). הפעלה אוטומטית רק ללא קול, אחרת הדפדפן יחסום אותה.
תמונות לפי וריאנטים
לכל וריאנט מוצר (צבע) יש סט תמונות משלו. בעת בחירת וריאנט:
- הגלריה עוברת לתמונות של אותו וריאנט
- אנימציית מעבר חלקה (crossfade או החלקה)
- התמונה הממוזערת של הוריאנט הנבחר מודגשת
במסד הנתונים: loading="eager". כאשר fetchpriority="high" — תמונות משותפות מוצגות עבור כל הוריאנטים.
מה כלול
- ניתוח תמונות קיימות והמלצות לצילום
- פיתוח צינור יצירת תמונות ממוזערות (libvips + S3 + CDN)
- יצירת רכיב גלריה עם זום ו-lightbox
- הגדרת טעינה עצלנית, טעינה מוקדמת ו-Core Web Vitals
- אינטגרציה עם CMS (WordPress, Shopify, Laravel) ווריאנטים של מוצר
- בדיקות על מכשירים אמיתיים (iPhone, Android, דפדפנים שונים)
- תיעוד ותמיכה לאחר השקה
טעויות נפוצות בפיתוח גלריות
- שימוש באותו גודל תמונה לכל ההקשרים (LCP כבד)
- חוסר בטעינה עצלנית לתמונות מחוץ למסך
- התעלמות מ-WebP (30-50% תעבורה נוספת)
- יישום זום שגוי: הגדלת התמונה כולה במקום הקטע (תוצאה מטושטשת)
- התנגשויות מחוות בנייד: צביטה לזום והחלקה לשינוי תמונות בו זמנית
לוחות זמנים ותמחור
| שלב | לוח זמנים |
|---|---|
| גלריה בסיסית (סליידר + תמונות ממוזערות + טעינה עצלנית) | 3–5 ימי עבודה |
| עם זום בהעברת עכבר ו-lightbox | 1–1.5 שבועות |
| עם צביטה לזום, וידאו, וריאנטים | 2–3 שבועות |
| צינור תצוגה מקדימה (מאפס) | +שבוע |
התמחור מתחיל ב-$1,500 לגלריה בסיסית וגדל עם המורכבות. צרו קשר להערכת פרויקט — נכין הצעה מסחרית בהתחשב בטכנולוגיה ובדרישות שלכם.







