לוח ציור שיתופי בזמן אמת לאפליקציות מובייל
כאשר משתמשים מרובים מציירים בו-זמנית על אותו לוח, נוצרים קונפליקטים של שינויים. אנו פותרים זאת באמצעות CRDT (סוגי נתונים משוכפלים ללא קונפליקטים) — סנכרון קנבס בזמן אמת ללא נעילות. אנו מפתחים לוחות ציור מוכנים לשימוש על Flutter או React Native עם חביון כמעט אפסי. נאמוד את הפרויקט שלך ביום אחד, עם תקציבים טיפוסיים הנעים בין 50,000 ל-150,000 דולר. לקוחות חוסכים בממוצע 30,000 עד 50,000 דולר על ידי בחירת ערכת ה-SDK שלנו ללוח ציור במקום פיתוח מותאם אישית.
לוח ציור שיתופי הוא קנבס אינסופי עם אובייקטים: קווים, צורות, מדבקות, תמונות, טקסט. משתמשים מציירים עם אצבע או חרט, עם תמיכה ברגישות ללחיצה, ורואים את פעולות האחרים בזמן אמת. במובייל, האתגרים המרכזיים הם קלט אצבע, צביטה לזום, ורינדור תחת עומס. אנו מבטיחים פעולה יציבה עם 100+ משתמשים בו-זמנית.
ארכיטקטורת נתונים: מודל אובייקטים של הקנבס
כל אובייקט על הלוח הוא מסמך במצב מבוזר. התאמה טובה היא Y.Map, שבו המפתח הוא מזהה ה-UUID של האובייקט, והערך הוא המאפיינים שלו (סוג, קואורדינטות, גודל, צבע, z-index). עבור אובייקטים מורכבים (נתיב מנקודות) — Y.Array של נקודות בתוך האובייקט.
const yobjects = ydoc.getMap('objects');
// Добавляем стрелку
yobjects.set(uuid(), {
type: 'arrow',
x1: 100,
y1: 200,
x2: 400,
y2: 350,
strokeColor: '#1a1a2e',
strokeWidth: 2
});הזזה בו-זמנית של אותו אובייקט על ידי שני משתמשים: Last-Write-Wins — מקובל עבור קואורדינטות. מחיקה בו-זמנית על ידי משתמש אחד ושינוי על ידי אחר — בעיית CRDT סטנדרטית: פעולת השינוי חלה על אובייקט שנמחק ואובדת. יש צורך בלוגיקת tombstone או אחסון זמני של אובייקטי "רפאים".
ציור מכחול: הזרמת נקודות
ציור חופשי מייצר 60–120 נקודות בשנייה על מסך 60Hz. שליחת כל נקודה דרך WebSocket היא מוגזמת. אופטימיזציה:
- ביטול חציצה (Buffering) — שליחת קבוצת נקודות כל 50ms.
- אלגוריתם פישוט — Douglas-Peucker מסיר נקודות מיותרות עם epsilon הניתן להגדרה. עקומה של 500 נקודות נדחסת ל-30–50 ללא אובדן איכות נראה, הפחתה של 90–95% (השגת יחס דחיסה של פי 10).
- חיזוי משיכות — ב-iOS עם Apple Pencil, UITouch.predictedTouches חוזה נקודות הבאות, ומפחית חביון נתפס.
משיכה כאובייקט CRDT: התחלה עם Y.Array זמני של נקודות ב-Awareness (לא במסמך — אין צורך בהיסטוריה מלאה של כל נקודה). ב-touchEnd, ביצוע commit לנתיב המפושט לתוך המסמך כאובייקט יחיד. זה מפחית תעבורה בעד 90%.
בחירת טכנולוגיית רינדור
מחסנית הרינדור תלויה בפלטפורמה ובדרישות הביצועים:
| פלטפורמה | טכנולוגיה | ביצועים | מורכבות |
|---|---|---|---|
| iOS Native | Metal + MetalKit | מקסימליים | גבוהה |
| Android Native | OpenGL ES / Vulkan | גבוהים | גבוהה |
| Flutter | CustomPainter | גבוהים | בינונית |
| React Native | react-native-skia | בינוניים | בינונית |
React Native: react-native-skia (מנוע גרפיקה Skia) הוא האפשרות הטובה ביותר לרינדור 2D יעיל. @shopify/react-native-skia תומך ב-Path, Paint, Text, Image. react-native-svg פשוט יותר אך איטי יותר עבור אובייקטים מונפשים. Metal עולה על OpenGL ES בעד 40% בקצב פריימים במכשירי iOS.
Flutter: CustomPainter עם Canvas API הוא הנתיב הטבעי. לייצור אנו משתמשים ב-CustomPainter מותאם עם אופטימיזציית dirty-region (ציור מחדש רק של האזור ששונה באמצעות Canvas.clipRect).
iOS Native: Metal + MetalKit לביצועים מקסימליים, UIBezierPath + CALayer למורכבות בינונית. Apple PencilKit הוא רכיב מוכן עם תמיכה ב-Pencil אך התאמה אישית מוגבלת.
Android Native: Canvas API עם Path למקרים פשוטים, OpenGL ES / Vulkan דרך GLSurfaceView למקרים מורכבים.
וירטואליזציה של קנבס אינסופי
עם 1000+ אובייקטים, רינדור כל הקנבס בכל פריים הוא בעיה. יש צורך באינדקס מרחבי (R-tree או רשת פשוטה מבוססת תאים) כדי לקבוע אובייקטים באזור התצוגה הנוכחי. יש לרנדר רק אובייקטים גלויים בתוספת חיץ קטן מעבר לקצוות אזור התצוגה. בדרך כלל רק כ-10% מהאובייקטים נמצאים באזור התצוגה בכל רגע. rbush היא ספריית R-tree ב-JavaScript שעובדת ב-React Native. אינדוקס מרחבי R-tree מפחית את זמן השאילתה ב-80% בהשוואה לבדיקה גסה (brute-force).
אסטרטגיית סנכרון: נתונים לשליחה ותזמון
מודעות לסמן/אזור תצוגה (מיקום המשתמש על הקנבס) — דרך Y.js Awareness, לא במסמך, 10fps מספיק.
אובייקטים בתהליך — שני מצבים:
- תצוגה מקדימה זמנית דרך Awareness (אחרים רואים את האובייקט שלא בוצע לו commit).
- רק האובייקט הסופי לאחר touchEnd (פשוט יותר אך ללא תצוגה מקדימה של ציור בזמן אמת).
המצב הראשון נותן חוויית משתמש טובה יותר, השני — פחות תעבורה ומורכבות.
CRDT עדיף על OT
CRDT (Yjs) מספק פתרון קונפליקטים אוטומטי ללא מתאם שרת. OT דורש שרת מרכזי כדי לסדר פעולות, מה שמגדיל חביון בחיבורים חלשים. CRDT מהיר פי 2 מ-OT בתנאי רשת לא יציבים, קריטי לאפליקציות מובייל. למידע נוסף על CRDT ו-Yjs.
איך אנו מפתחים לוחות ציור: שלב אחר שלב
- ניתוח דרישות — הגדרת מחסנית טכנולוגית, עיצוב מודל אובייקטים ותרחישי סנכרון.
- עיצוב ארכיטקטורה — סכמת נתונים, בחירת ספריית CRDT (Yjs), הגדרת WebSocket/WebRTC.
- יישום כלים — מכחול, צורות, טקסט, תמונות עם אופטימיזציית רינדור.
- שילוב סנכרון — חיבור Yjs, בדיקת קונפליקטים.
- אופטימיזציית ביצועים — וירטואליזציית קנבס, דחיסת נתונים (עד 95% לנתיבים), הפחתת חביון ל-50ms.
- בדיקות על מכשירים אמיתיים — iOS/Android, גרסאות מערכת הפעלה שונות, בדיקות עומס עם 100+ משתמשים.
- פריסה ותמיכה — פרסום ל-App Store/Google Play, תיעוד API.
טעויות טיפוסיות ביישום לוח ציור
- הזנחת דחיסת נתיבים — שליחת כל הנקודות מעמיסה על הערוץ. השתמשו ב-Douglas-Peucker.
- שימוש ב-OT לסנכרון — מגדיל חביון ודורש שרת. בחרו ב-CRDT.
- רינדור כל הקנבס — קצב הפריימים יורד עם 500+ אובייקטים. תמיד יישמו וירטואליזציה.
- התעלמות מחיזוי Apple Pencil — חביון מיותר ב-iOS.
שלבים ולוחות זמנים
| שלב | משך |
|---|---|
| ניתוח ועיצוב | 2–4 שבועות |
| פיתוח פונקציונליות בסיסית | 6–10 שבועות |
| כלים מתקדמים (מכחול, וירטואליזציה) | 8–16 שבועות |
| בדיקות ופריסה | 2–4 שבועות |
לוח ציור בסיסי (צורות, טקסט, חיצים, סנכרון) על Flutter או React Native — 10–16 שבועות. עם ציור מכחול, רגישות ללחיצה, פישוט נתיבים חכם ווירטואליזציית קנבס — 20–32 שבועות. התמחור מתחיל מ-50,000 דולר ללוח ציור בסיסי ו-100,000 דולר לתכונות מתקדמות, בהתאם למורכבות.
מה כלול בפיתוח לוח ציור
- תיעוד ארכיטקטורה ו-API.
- קוד מקור במאגר עם CI/CD.
- שילוב עם מערכת האימות שלך (אופציונלי).
- הכשרת צוות הלקוח.
- תמיכה באחריות ל-3 חודשים.
מידע נוסף על אופטימיזציית רינדור
אנו משתמשים באופטימיזציית dirty-region, וציור מחדש רק של אזורים ששונו בקנבס. זה מפחית עומס על ה-GPU ומעלה את קצב הפריימים.צרו קשר כדי לאמוד את הפרויקט שלכם, עם עלויות טיפוסיות הנעות בין 50,000 ל-150,000 דולר. אנו נבחר את המחסנית הטכנולוגית האופטימלית ונציע לוחות זמנים אישיים.







