הטמעת נוכחות סמן (סמני משתמש) באתר שלך
תארו לעצמכם: עשרה עורכים עובדים על מסמך אחד, והסמן של כל משתמש קופץ, נעלם או חופף. זהו מצב טיפוסי ללא הטמעת נוכחות סמן תקינה. נוכחות סמן—הצגת סמנים ובחירות בזמן אמת—נראית פשוטה, אך יש הרבה מלכודות: העברת קואורדינטות יעילה, אנימציה חלקה, מיפוי נכון במהלך גלילה וניתוק. ללא אופטימיזציה, התעבורה יכולה להגיע ל-10,000 הודעות בשנייה עם 50 משתמשים, מה שקריטי לרשתות סלולריות ומעלה את עלויות השרת בעד 50% (כ-$70 לחודש עבור 10 משתמשים). לצוות שלנו ניסיון של חמש שנים בשיתוף פעולה בזמן אמת והטמענו נוכחות סמן עבור 30+ פרויקטים (עורכים, לוחות, צ'אטים). אנו מציעים פתרון מפתח: תוך 1–2 ימים אנו משלבים נוכחות משתמש עם אופטימיזציית תעבורה והבטחת חלקות. צרו קשר לייעוץ והדגמה.
Throttle והשפעתו על התעבורה
אירוע ה-mousemove נורה 50–100 פעמים בשנייה. שליחת כל אחד לשרת, עם 10 משתמשים, מביאה לתעבורה של אלפי הודעות בשנייה. Throttle אופטימלי הוא 30 fps (33 אלפיות השנייה). השתמשו בפונקציית throttle—כמו מ-lodash-es:
import { throttle } from 'lodash-es';
const sendCursor = throttle((x: number, y: number) => {
socket.emit('cursor:move', { x, y });
}, 33);
document.addEventListener('mousemove', (e) => {
sendCursor(e.clientX, e.clientY);
});
document.addEventListener('mouseleave', () => {
socket.emit('cursor:leave');
});בשרת (Socket.IO)—שדרו לחדר, ללא השולח:
socket.on('cursor:move', (data: { x: number; y: number }) => {
socket.to(roomId).emit('cursor:update', {
userId: socket.data.userId,
...data,
});
});
socket.on('cursor:leave', () => {
socket.to(roomId).emit('cursor:remove', {
userId: socket.data.userId,
});
});
socket.on('disconnect', () => {
socket.to(roomId).emit('cursor:remove', {
userId: socket.data.userId,
});
});
חיסכון בתעבורה עם 30 fps במקום 60 הוא עד 50%—קריטי לרשתות סלולריות.
חישוב חיסכון בתעבורה
ב-50 הודעות בשנייה למשתמש ו-10 משתמשים, התעבורה היא 500 הודעות/שנייה. Throttle ל-30 fps מפחית ל-300 הודעות/שנייה. חיסכון של 40%—כ-$30–40 לחודש בתעריפים הנוכחיים.למה אינטרפולציית Lerp היא חובה?
עדכוני מיקום ישירים לכל אירוע גורמים לקפיצות. מעבר CSS import { throttle } from 'lodash-es'; const sendCursor = throttle((x: number, y: number) => { socket.emit('cursor:move', { x, y }); }, 33); document.addEventListener('mousemove', (e) => { sendCursor(e.clientX, e.clientY); }); document.addEventListener('mouseleave', () => { socket.emit('cursor:leave'); }); יוצר פיגור בכיוון הלא נכון. הפתרון הנכון היחיד הוא אינטרפולציה לינארית (lerp) בתוך requestAnimationFrame:
interface RemoteCursor {
userId: string;
name: string;
color: string;
current: { x: number; y: number };
target: { x: number; y: number };
el: HTMLElement;
}
const cursors = new Map<string, RemoteCursor>();
function lerp(a: number, b: number, t: number) {
return a + (b - a) * t;
}
function animateCursors() {
cursors.forEach((cursor) => {
cursor.current.x = lerp(cursor.current.x, cursor.target.x, 0.35);
cursor.current.y = lerp(cursor.current.y, cursor.target.y, 0.35);
cursor.el.style.transform = `translate(${cursor.current.x}px, ${cursor.current.y}px)`;
});
requestAnimationFrame(animateCursors);
}
animateCursors();
// При получении обновления — только меняем target
socket.on('cursor:update', ({ userId, x, y, name, color }) => {
if (!cursors.has(userId)) {
const el = createCursorElement(userId, name, color);
document.body.appendChild(el);
cursors.set(userId, {
userId,
name,
color,
current: { x, y },
target: { x, y },
el,
});
} else {
cursors.get(userId)!.target = { x, y };
}
});
מקדם אינטרפולציה של 0.35 נותן תנועה חלקה ללא פיגור מורגש.
השוואת גישות: WebSocket לעומת Yjs Awareness
| קריטריון | WebSocket פשוט | Yjs Awareness |
|---|---|---|
| זמן הטמעה | 1–2 ימים מאפס | חצי יום אם Yjs כבר בשימוש |
| עומס על השרת | גבוה יותר (לוגיקת שידור עצמית) | נמוך יותר (שידור מובנה) |
| קלות הרחבה | בינונית (הוספת שדות ידנית) | נמוכה (מצב אוטומטי) |
| תמיכה בבחירת טקסט | דורש לוגיקה נוספת | מובנה דרך Awareness |
Yjs Awareness מהיר פי 3 להטמעה מאשר WebSocket פשוט אם Yjs כבר בשימוש. תקן ה-WebSocket (ראו RFC 6455) מגדיר פרוטוקול תקשורת דו-כיווני.
איך לבחור בין WebSocket ל-Yjs Awareness?
אם Yjs כבר בשימוש בפרויקט—השתמשו ב-Awareness: הוא מסנכרן אוטומטית מצב סמן, בחירת טקסט ושדות אחרים. אחרת, WebSocket פשוט מספיק. לעיון: WebSocket הוא פרוטוקול תקני, ו-Yjs היא ספריית CRDT לעריכה שיתופית.
תהליך העבודה
- ניתוח — הגדרת מקרי שימוש, עומס, מחסנית נוכחית.
- עיצוב — בחירת פרוטוקול (WebSocket או Yjs), עיצוב מודל נתונים.
- הטמעה — כתיבת חלק לקוח-שרת, שילוב throttle, lerp, TTL.
- בדיקות — בדיקה עם 50+ משתמשים בו-זמנית, מדידת תעבורה.
- פריסה ותמיכה — פריסה על האירוח שלכם, הצעת אחריות ל-30 יום.
מה לעשות אם סמנים נעלמים במהלך גלילה?
שמרו קואורדינטות יחסית למסמך (clientX + scrollOffset), ואז הפחיתו את הגלילה הנוכחית בעת רינדור. אחרת, הסמנים של אחרים יזוזו במהלך גלילה.
document.addEventListener('mousemove', throttle((e) => {
provider.awareness.setLocalStateField('cursor', {
x: e.clientX + window.scrollX,
y: e.clientY + window.scrollY,
});
}, 33));
function getCursorViewportPos(docX: number, docY: number) {
return {
x: docX - window.scrollX,
y: docY - window.scrollY,
};
} TTL וניקוי סמנים מיושנים
אם משתמש סוגר טאב ללא ניתוק מפורש, הסמן נשאר על המסך. פתרון—heartbeat + TTL בצד הלקוח:
const CURSOR_TTL = 5000; // 5 секунд без обновлений
const lastSeen = new Map<string, number>();
socket.on('cursor:update', ({ userId, ...pos }) => {
lastSeen.set(userId, Date.now());
updateCursor(userId, pos);
});
setInterval(() => {
const now = Date.now();
lastSeen.forEach((ts, userId) => {
if (now - ts > CURSOR_TTL) {
removeCursor(userId);
lastSeen.delete(userId);
}
});
}, 1000);
השוואת אפשרויות Throttle
| תדר (fps) | תעבורה עבור 10 משתמשים (הודעות/שנייה) | חלקות |
|---|---|---|
| 60 | 600 | מושלמת |
| 30 | 300 | מספקת |
| 15 | 150 | רעידות מורגשות |
עבור רוב המשימות, 30 fps הוא האיזון האופטימלי.
מה כלול בעבודה?
בהזמנת הטמעת נוכחות סמן, אתם מקבלים:
- ארכיטקטורת העברת נתונים (WebSocket או Yjs)
- קוד צד לקוח עם throttle ואינטרפולציית lerp
- טיפול בקואורדינטות (יחסית למסמך, גלילה)
- מנגנון TTL להסרת סמנים מיושנים
- אינטגרציה עם מערכת קיימת (עורך, צ'אט וכו')
- תיעוד והדרכת צוות
הטמעה מאפס אורכת 1–2 ימים. אם Yjs או Socket.IO כבר בשימוש, חצי יום. צרו קשר לייעוץ: נעריך את הפרויקט שלכם ונציע את הפתרון האופטימלי. במשך חמש שנים השלמנו יותר מ-30 אינטגרציות—הפרויקט שלכם יהיה בידיים טובות. הזמינו הטמעה היום וקבלו בדיקות חינם.







