הטמעת לוח לבן שיתופי לאתר שלך

עריכת לוח שיתופי בזמן אמת דורשת סנכרון מורכב ורינדור מהיר—ללא הגישה הנכונה, הפרויקט עלול להיתקע בביצות של באגים. אנחנו בונים לוחות לבן במפתח מלא: בוחרים את הסטACK האופטימלי, מיישמים סנכרון CRDT, ומגדירים הכול לביצועים יציבים. הצוות שלנו מטפל בכל המחזור—מרעיון ועד תמיכה—כדי שתקבלו כלי אמין לעבודת צוות.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
הטמעת לוח לבן שיתופי לאתר שלך
מורכב
~2-4 שבועות

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1306
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1049
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

הטמעת לוח שיתופי לאתר שלך

סנכרון שינויים במהלך עריכה שיתופית של לוח הוא אחד האתגרים הקשים ביותר בפיתוח אתרים. ללא מחסנית רינדור ומנגנון סנכרון מתאימים, הפרויקט שלך עלול לסבול מבאגים וביצועים ירודים. אנו בונים לוחות מקוונים לשיתוף פעולה בצוות: ציור חופשי, פתקיות דביקות, צורות, קווים וסנכרון בזמן אמת. המהנדסים שלנו מיישמים לוחות מאפס או משלבים SDK קיימים (tldraw, Excalidraw) עם סנכרון CRDT מותאם אישית. נמסרו מעל 30 פרויקטים—מלוחות הערות פשוטים ועד לעורכים שיתופיים מלאים עם WebRTC ושיחות שמע. צוותים המשתמשים בגישה שלנו חוסכים עד 40% מזמן הפיתוח בהשוואה לבנייה פנימית.

בחירת מחסנית הרינדור הנכונה

אפשרות זמן ל-MVP רמת התאמה אישית ביצועים (1000 צורות) עלות תחזוקה
tldraw 1–2 ימים בינונית (SDK) גבוהה (Canvas) נמוכה (קוד פתוח)
Konva.js 3–4 שבועות מלאה גבוהה (Canvas) בינונית
Excalidraw 1–2 ימים (פיצול) נמוכה (הפיצול מורכב) בינונית (SVG) גבוהה (פיצול)
Fabric.js 2–3 שבועות בינונית בינונית (Canvas) גבוהה (מיושן)
SVG + vanilla 1–2 שבועות מלאה נמוכה מעל 500 צורות בינונית

למוצר ייצור עם דרישות מותאמות אישית, אנו מעדיפים את tldraw כבסיס או את Konva.js מאפס. פיצולים של Excalidraw אפשריים אך עלות התחזוקה גבוהה. אנו בוחרים את המחסנית בהתאם למקרה השימוש ולתקציב שלך.

מדוע CRDT הוא אופטימלי לסנכרון

CRDT (סוג נתונים משוכפל ללא התנגשויות) פותר אוטומטית התנגשויות עריכה ללא שרת מרכזי. עבור לוחות אנו משתמשים ב-Yjs, ספריית CRDT המותאמת לעריכה שיתופית. Yjs מהיר עד פי 3 מ-OT (טרנספורמציה אופרטיבית) במבחני ביצועים עם 10 משתמשים בו-זמנית—הבדל אמיתי בעת עריכה של לוח עמוס.

ארכיטקטורה: מצב הלוח

לוח הוא אוסף של אובייקטי צורה. לכל צורה יש סוג, גיאומטריה וסגנון.

סוגי צורות נפוצים
סוג מאפיינים
מלבן x, y, width, height, fill, stroke, strokeWidth, cornerRadius
אליפסה x, y, radiusX, radiusY, fill, stroke
קו points, stroke, strokeWidth
חץ points, stroke, headStyle
טקסט content, fontSize, fontFamily, color, width
ציור חופשי points, stroke, strokeWidth, pressure
תמונה src, x, y, width, height

CRDT לסנכרון צורות

Y.Map מושלם לאחסון כל צורה לפי המזהה שלה:

import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

const ydoc = new Y.Doc();
const yshapes = ydoc.getMap<Y.Map<unknown>>('shapes');
const provider = new WebsocketProvider(
  process.env.WS_URL!,
  `board-${boardId}`,
  ydoc
);

// Добавление shape
function addShape(shape: Shape) {
  const yshape = new Y.Map<unknown>(Object.entries(shape));
  yshapes.set(shape.id, yshape);
}

// Обновление (например, при перетаскивании)
function updateShape(id: string, patch: Partial<Shape>) {
  const yshape = yshapes.get(id);
  if (!yshape) return;
  ydoc.transact(() => {
    Object.entries(patch).forEach(([key, value]) => {
      yshape.set(key, value);
    });
  });
}

// Удаление
function deleteShape(id: string) {
  yshapes.delete(id);
}

// Реактивность
yshapes.observeDeep((events) => {
  events.forEach((event) => {
    rerenderCanvas(yshapes);
  });
});

ציור חופשי: אופטימיזציה של נקודות

ציור חופשי מייצר מאות נקודות. שליחת כולן היא יקרה. אנו משתמשים באלגוריתם Ramer-Douglas-Peucker כדי לפשט את העקומה:

function rdp(points: [number, number][], epsilon: number): [number, number][] {
  if (points.length < 3) return points;
  let maxDist = 0;
  let maxIdx = 0;
  const end = points.length - 1;
  for (let i = 1; i < end; i++) {
    const dist = perpendicularDistance(points[i], points[0], points[end]);
    if (dist > maxDist) {
      maxDist = dist;
      maxIdx = i;
    }
  }
  if (maxDist > epsilon) {
    const left = rdp(points.slice(0, maxIdx + 1), epsilon);
    const right = rdp(points.slice(maxIdx), epsilon);
    return [...left.slice(0, -1), ...right];
  }
  return [points[0], points[end]];
}

לאחר שהמשתמש מסיים לצייר, אנו מריצים פישוט ומסנכרנים את קבוצת הנקודות המצומצמת. לעקומות חלקות, אנו מסתמכים על ספריית import * as Y from 'yjs'; import { WebsocketProvider } from 'y-websocket'; const ydoc = new Y.Doc(); const yshapes = ydoc.getMap<Y.Map<unknown>>('shapes'); const provider = new WebsocketProvider( process.env.WS_URL!, `board-${boardId}`, ydoc ); // Добавление shape function addShape(shape: Shape) { const yshape = new Y.Map<unknown>(Object.entries(shape)); yshapes.set(shape.id, yshape); } // Обновление (например, при перетаскивании) function updateShape(id: string, patch: Partial<Shape>) { const yshape = yshapes.get(id); if (!yshape) return; ydoc.transact(() => { Object.entries(patch).forEach(([key, value]) => { yshape.set(key, value); }); }); } // Удаление function deleteShape(id: string) { yshapes.delete(id); } // Реактивность yshapes.observeDeep((events) => { events.forEach((event) => { rerenderCanvas(yshapes); }); }); .

תצוגה: פאן וזום

הלוח שלנו משתמש בקנבס אינסופי. כל הקואורדינטות הן במרחב עולמי; התצוגה מגדירה מה נראה:

interface Viewport {
  x: number; // смещение
  y: number;
  zoom: number; // 0.1 – 4.0
}

// Мировые координаты → экранные
function worldToScreen(wx: number, wy: number, vp: Viewport) {
  return {
    x: wx * vp.zoom + vp.x,
    y: wy * vp.zoom + vp.y,
  };
}

// Экранные → мировые (для pointer events)
function screenToWorld(sx: number, sy: number, vp: Viewport) {
  return {
    x: (sx - vp.x) / vp.zoom,
    y: (sy - vp.y) / vp.zoom,
  };
}

// Zoom к точке (pinch или колёсико)
function zoomAt(vp: Viewport, screenX: number, screenY: number, delta: number): Viewport {
  const factor = delta > 0 ? 1.1 : 0.9;
  const newZoom = Math.max(0.1, Math.min(4.0, vp.zoom * factor));
  const zoomRatio = newZoom / vp.zoom;
  return {
    x: screenX - (screenX - vp.x) * zoomRatio,
    y: screenY - (screenY - vp.y) * zoomRatio,
    zoom: newZoom,
  };
}

Canvas לעומת SVG: בחירה תחת עומס

מתחת ל-500 צורות, SVG עובד מצוין ומפשט את בדיקת הפגיעות. מעל 1000 צורות עם ציור פעיל, אנו עוברים ל-Canvas (2D) או ל-WebGL באמצעות Pixi.js. גישה היברידית מציגה צורות על Canvas ואלמנטים של ממשק משתמש ב-HTML מעל.

מקרה קונקרטי: פלטפורמת Ed-Tech

עבור לקוח ed-tech, בנינו לוח שיתופי המשמש מורים ותלמידים. בחרנו ב-tldraw לאב-טיפוס מהיר, ואז החלפנו את שכבת הסנכרון שלו ב-Yjs כדי להתמודד עם עד 30 משתמשים בו-זמנית. התוצאה: זמן השהיית הסנכרון ירד מ-~200 אלפיות השנייה לפחות מ-50 אלפיות השנייה, והלוח נשאר מגיב גם עם 500+ צורות. הפרויקט עבר מעיצוב לייצור תוך 10 ימים.

יישום Undo / Redo

Yjs מספק UndoManager שמקבץ פעולות בחלון של 500 אלפיות השנייה:

const undoManager = new Y.UndoManager(yshapes, {
  trackedOrigins: new Set([ydoc.clientID]),
  captureTimeout: 500,
});

document.addEventListener('keydown', (e) => {
  if (e.ctrlKey && e.key === 'z') undoManager.undo();
  if (e.ctrlKey && e.key === 'y') undoManager.redo();
});

ייצוא הלוח ל-PNG

async function exportToPNG(boardId: string): Promise<Blob> {
  const canvas = document.getElementById('whiteboard-canvas') as HTMLCanvasElement;
  const shapes = Array.from(yshapes.values()).map(s => shapeFromYMap(s));
  const bbox = getBoundingBox(shapes);
  const offscreen = new OffscreenCanvas(bbox.width + 80, bbox.height + 80);
  const ctx = offscreen.getContext('2d')!;
  ctx.fillStyle = '#ffffff';
  ctx.fillRect(0, 0, offscreen.width, offscreen.height);
  renderShapes(ctx, shapes, { x: -bbox.x + 40, y: -bbox.y + 40, zoom: 1 });
  return await offscreen.convertToBlob({ type: 'image/png' });
}

תהליך העבודה שלנו

  1. ניתוח: הבנת תרחישי משתמש, בחירת המחסנית הנכונה.
  2. עיצוב: ארכיטקטורת נתונים, פרוטוקול סנכרון, פריסת ממשק משתמש.
  3. יישום: בניית מנוע קנבס, שילוב Yjs, יצירת כלים מותאמים אישית.
  4. בדיקות: בדיקות יחידה, בדיקות עומס, אימות סנכרון בין משתמשים.
  5. פריסה: תמונת Docker, הגדרת WebSocket, CDN לנכסים סטטיים.

הערכות לוחות זמנים

  • שילוב tldraw עם סנכרון Yjs מותאם אישית: 5–7 ימים.
  • לוח מלא מאפס (Konva.js + ציור חופשי, צורות, תצוגה, ביטול, נוכחות, ייצוא): 3–4 שבועות.
  • הוספת WebRTC לוידאו/שמע לצד הלוח: +שבוע.
  • כל הקוד מגיע עם אחריות חינם לתיקון באגים למשך חודש.

מה כלול

  • תיעוד ארכיטקטוני (בחירת מחסנית, סכמת נתונים, פרוטוקול סנכרון).
  • קוד מקור מלא של הלוח עם הערות ובדיקות למודולים קריטיים.
  • שילוב עם מערכת האימות וה-API שלך.
  • פריסה (תמונת Docker, הגדרת WebSocket, CDN).
  • סדנת צוות על התאמה אישית ותחזוקה.
  • אחריות חינם לתיקון באגים למשך חודש.
  • מהנדסים מוסמכים עם ניסיון עמוק בשיתוף פעולה בזמן אמת.

קבל ייעוץ מהמהנדסים שלנו—נעריך את הפרויקט שלך ונציע את הפתרון האופטימלי. צור קשר כדי לדון בפרטים.