צור ושנה גרפיקת SVG של נתונים מחיישנים המחוברים ל-IoT עם JavaScript
בחלק האחרון של סדרת המאמרים בנושא ציור גרפיקה עם נתונים מחיישנים המחוברים לאינטרנט של הדברים, הגיע הזמן לדבר על איך ליצור או לשנות עם JavaScript ציורים בפורמט SVG וכמה מהאלמנטים HTML המשמשים כמיכל או שמציגים מידע משלים לגרפיקה.
משתמשי היעד של הדרכה זו אמורים ליצור פרופיל אלקטרוניקה ותכנות מחשבים. מיקרו-בקרים, אולי הם לא מכירים HTML, CSS o SVG; מסיבה זו, בפרקים הקודמים בוצעה היכרות קצרה עם השפה או הטכנולוגיה המקבילה. בחלק אחרון זה הגישה מעט שונה, מכיוון שהקוראים בוודאי יודעים לתכנת, ייתכן ששימוש בשפה C + + זה, איך JavaScript, חולק תחביר בסיסי עם C והוא יכול לשמש כרפרנס כדי לדלג על רוב מושגי התכנות הבסיסיים ובכך להתמקד בהבדלים ובשימוש הספציפי שמעניין אותנו ליצירת גרפיקת חיישנים ב-IoT.
השם נותן רמז להבדל הראשון: JavaScript זו שפת תכנות תסריט (מקף) וככזה, זה כן פירש, אין צורך להדר אותו; ההקשר שבו ה תסריט (דפדפן אינטרנט, למשל) יקרא, יתרגם ויבצע את הפקודות. ליתר דיוק, ברוב המקרים יש א קומפילציה של זמן ריצה (JIT), אלא לתהליך כתיבת הקוד JavaScript זה לא משפיע עלינו, אנחנו פשוט כותבים את הקוד וזה יכול לעבוד.
השם מכיל גם את הבלבול הראשון: JavaScript אין שום קשר קל עם Java. בתחילה, כאשר הוא פותח נטסקייפ לדפדפן שלו, הוא נקרא תחילה Mocha ואחר כך LiveScript הפחות מבלבל. לאחר היישום המוצלח שלו בדפדפנים, וחורג מהם, הוא היה סטנדרטי כ ECMAScript (כדי ECMA-262, גרסה 6 בזמן הכתיבה) להפוך לנייטרלי ביחס לדפדפנים שמיישמים זאת. נכון לעכשיו יש גם תקן ISO מגרסה 5, 2011 (ISO / IEC 16262: 2011 בזמן כתיבת המאמר)
משתנים, סוגי נתונים בסיסיים ואובייקטים ב-JavaScript
בניגוד למה שקורה, למשל, ב C + +, en JavaScript סוג הנתונים אינו כלול בעת הצהרת משתנה וגם הסוג המשויך למשתנה אינו קבוע, ניתן להקצות ערך מסוג אחר לאורך כל ביצוע התוכנית.
1
2
3
4
5
6
7
|
var cosa;
cosa=“texto”;
console.log(typeof cosa); // Debería mostrar string en la consola
cosa=123;
console.log(typeof cosa); // Debería mostrar number en la consola
cosa={temperatura:22,corriente:1.5};
console.log(typeof cosa); // Debería mostrar object en la consola
|
בדוגמה הקודמת, המשתנה "דבר" הוכרז (ללא ציון סוג הנתונים), לאחר מכן מוקצים נתונים מסוג אחר ומתייעצים עם typeof
הסוג ש JavaScript שהוא פירש. כדי לנפות באגים בקוד ניתן לכתוב אותו במסוף המפקח של דפדפן האינטרנט (שלא ישפיע על הצגת האינטרנט) עם console.log()
.
כדי לאלץ המרת נתונים לסוג מסוים, במיוחד טקסט למספרי, ניתן להשתמש בפונקציות כגון parseInt()
o parseFloat()
הממירים למספרים שלמים או למספרי נקודה צפה בהתאמה. אפשר לעשות את ההמרה ההפוכה String()
, אם כי לא סביר שיהיה צורך בהמרה אוטומטית בדרך כלל מספיקה. עם parseFloat()
לדוגמה, אתה יכול לקבל את הערך של מאפיין דף אינטרנט, כגון רוחב או גובה של אובייקט, הכולל יחידות; בדרך זו, הביטוי parseFloat("50px");
יחזיר 50, ערך מספרי, כתוצאה מכך.
En JavaScript אין הבחנה בין מרכאות כפולות ליחידות; סוג הנתונים בשני המקרים הוא string
, וכל אחד מהם יכול לכלול את השני ללא צורך בקודי בריחה.
1
2
3
4
5
6
7
8
9
10
|
var texto;
console.log(typeof texto); // Debería mostrar string en la undefined
texto=“esto es un texto”;
console.log(typeof texto); // Debería mostrar string en la consola
texto=‘A’;
console.log(typeof texto); // Debería mostrar string en la consola
texto=“esto es un ‘texto'”;
console.log(typeof texto); // Debería mostrar string en la consola
texto=‘”A”‘;
console.log(typeof texto); // Debería mostrar string en la consola
|
בדוגמה הקודמת ניתן לראות שמשתנה, כאשר הוא הוכרז (קיים) אך לא הוקצה לו ערך כלשהו, מכיל סוג נתונים לא מוגדר (undefined
). לאובייקט שלא הוקצה יש את הערך null
; כלומר, האובייקט קיים, אך ללא ערך; למשתנה שהתייחס אליו לא יהיה א typeof
undefined
אבל object
. אובייקט יכול להיות גם ריק, כלומר, לא null אבל אין לו מאפיינים כלשהם.
כדי להגדיר אובייקט ב JavaScript סגורים בסוגרים ({
y }
) המאפיינים או השיטות, מופרדים על ידי סימן המעי הגס (:
) שם מאפיין ערך מאפיין ולפי פסיק (,
) המאפיינים השונים. תוכל למצוא מידע נוסף על דרך זו לביטוי אובייקט במאמר על פורמט JSON.
למרות שאתה יכול להשתמש בתחביר שעלול לגרום לך לחשוב אחרת, en JavaScript אין שיעורים אלא אבות טיפוסכלומר, כדי שאובייקט יירש מאפיינים ושיטות, נוצר אובייקט נוסף (אב הטיפוס) שהאחרים (הילדים) משתמשים בו בתור התייחסות. התחביר הקרוב ביותר לסגנון של JavaScript להשתמש באב טיפוס הוא Object.create
אם כי זה גם אפשרי (ולפעמים שימושי) להשתמש new
כמו בשפות אחרות מוכוונות עצמים.
1
2
3
4
|
var perro=new Mamifero(); // Esto funciona, pero no es exactamente el nuevo estilo JavaScript
console.log(perro instanceof Mamifero);
var gato=Object.create(Mamifero); // Crear un objeto usando un prototipo al estilo JavaScript
console.log(Mamifero.isPrototypeOf(gato));
|
כדי שאילתה אם אובייקט אחד הוא מופע של אחר, אם אתה משתמש בו כאב טיפוס, אם אתה יורש את המאפיינים שלו, בקיצור, אתה יכול להשתמש instanceof
(נוצר עם new
) או isPrototypeOf
(נוצר עם Object.create
) אשר יוערך ל-true כאשר האובייקט משתמש באב הטיפוס ול-false כאשר הוא לא עושה זאת.
ברגע שאובייקט נוצר תוך שימוש באחר בתור אב טיפוס, כלומר, ברגע שאובייקט נוצר, הוא יכול להיות להוסיף מאפיינים חדשים או לעקוף מאפייני אב טיפוס שימוש בתחביר נקודות כמו ב gato.peso=2.5
.
La מערכים ב JavaScript הם שונים מאלה שאתה בטח מכיר בהם C. מלכתחילה, הם מוכרזים ללא צורך לציין את אורכם, רק עם סימני פתיחה וסגירה של סוגריים מרובעים ([
y ]
), רכיבים יכולים להיות הטרוגניים (סוגי נתונים שונים באותו מערך) וניתן להוסיף אלמנטים חדשים מבלי להיות מוגבלים למגבלה. המטריצות של JavaScript הם למעשה רשימות (אוספים) של אלמנטים אליהם הפניה באמצעות אינדקס מספרי או בשם. מערך יכול להכיל בו זמנית אינדקסים מספריים ושמות אלמנטים, אך מקובל להשתמש באובייקטים (מאפיינים) כדי לנצל את הסוג השני.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// Declarar matrices (arrays)
var preparada=[]; // La matriz ha sido declarada pero (todavía) no contiene valores
var cosas=[“silla”,“mesa”,“caja”]; // Matriz declarada con componentes formada por cadenas de texto
var valores=[200,“lleno”,0.5,true,“simple”,false,false,10]; // Matriz declarada con componentes heterogéneos
var ramas=[20,“abc”,[1,2,3],false,[10,20,[“uno”,“dos”]]]; // Matriz que contiene matrices
var demode=new Array(10,20,30,4,3,2,1); // La sintaxis con new no es la preferida de JavaScript aunque funciona…
var peligrosa=new Array(10); // …pero con el riesgo de confundir índices con elementos: la matriz peligrosa tiene 10 elementos, no un elemento de valor 10
// Acceder a los valores de la matriz
preparada.push(33.33); // Añade un nuevo valor al final de la matriz
console.log(“La matriz ‘preparada’ contiene “+preparada.length+” elementos”); // Ahora contine 1 elemento
console.log(cosas[0]); // Muestra en la consola el primer valor de la matriz (las matrices empiezan en el índice cero)
cosas[2]=“tarro”;
preparada[10]=50; // Los índices no tienen que ser consecutivos
console.log(“La matriz ‘preparada’ contiene “+preparada.length+” elementos”); // Ahora contine 11 elementos
console.log(“Elemento sexto: “+preparada[5]); // undefined
// Verificar si una variable es (apunta a) una matriz
console.log(Array.isArray(cosas)); // Nuevas versiones de JavaScript (ECMAScript versión 5 o superior)
console.log(cosas instanceof Array); // Implementaciones de JavaScript (ECMAScript) más viejas
// Para esto es mejor usar objetos
var frutas=[];
frutas[“peras”]=20;
frutas[“manzanas”]=30;
frutas[4]=10;
console.log(frutas.peras);
console.log(frutas[“manzanas”]);
console.log(frutas[4]);
console.log(frutas[3]); // undefined
|
כפי שניתן לראות בדוגמה הקודמת, כדי לדעת אם משתנה מתאים למופע של מערך (זהו אובייקט מערך) ניתן להשתמש instanceof
, כפי שכבר נעשה שימוש עם אובייקטים גנריים או, בגרסאות עדכניות יותר של JavaScript אתה יכול לפנות אליו Array.isArray()
כדי לגשת לאלמנטים של המערך אתה יכול להשתמש באינדקס שלו (matriz[7]
) או לפי שם הנכס עם השם בסוגריים מרובעים (matriz["nombre"]
) או עם תחביר הנקודות הרגיל עבור אובייקטים (matriz.nombre
). מכיוון שהשם הוא מחרוזת טקסט, ניתן להשתמש בביטוי, כולל משתנים, לחיבורו. כדי לעבור דרך מערך עם מאפיינים, ניתן להשתמש בלולאה עם הפורמט for(propiedad in matriz)
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
var matriz=[];
matriz[“color”]=“verde”;
matriz[“grosor”]=10;
matriz[“estado”]=“nuevo”;
matriz[0]=25.0;
matriz[1]=“uno”;
for(propiedad in matriz)
{
console.log(propiedad+” valor “+matriz[propiedad]);
}
/* El resultado en la consola será:
0 valor 25
1 valor uno
color valor verde
grosor valor 10
estado valor nuevo
*/
|
זה מעניין עבור המטרה שלנו לטפל האובייקט Date
, שאיתו ניתן לייצג ולנהל תאריך ושעה JavaScript. ניתן ליצור את האובייקט ללא נתונים, כך שהוא ייקח את התאריך והשעה הנוכחיים, או שניתן ליצור אותו על ידי ציון תאריך כערך, בין אלפיות השנייה מאז 1 בינואר 1970 (כגון זמן יוניקס או זמן POSIX אך מבוטא באלפיות שניות במקום שניות) או ציון ערכים נפרדים של שנה, חודש, יום, שעה...
האובייקט כולל סדרה שלמה של שיטות לשאילתה או הגדרת התאריך והשעה:
-
now()
מחזירה את התאריך והשעה הנוכחיים המבוטאים באלפיות שניות מאז 1 בינואר 1970 -
getTime()
|setTime()
מקבל או משנה, בהתאמה, את ערך הזמן באלפיות שניות מאז 1 בינואר 1970.valueOf()
, שהיא שיטה הקיימת ברוב האובייקטים, מתקבל גם הערך של אובייקט ה-Date המתאים, כגוןgetTime()
עם זמן יוניקס או זמן POSIX מתבטא ב- ms. -
getMilliseconds()
|setMilliseconds()
משמש לשאילתה או להגדיר את החלק השברירי של אלפית השניות של האובייקטDate
שעליו הוא מבוצע. אם תייעצו, הערך המתקבל הוא בין 0 ל-999 אך ניתן להקצות ערכים גדולים יותר שיצטברו בתאריך ובשעה הכוללים ולכן, כמו שאר שיטות get, זה משמש להגדלת הערך של האובייקטDate
(או הקטינו אותו, אם נעשה שימוש בערכים שליליים). -
getSeconds()
|setSeconds()
מחזירה או משנה, בהתאמה, את ערך השניות של האובייקטDate
. -
getMinutes()
|setMinutes()
משמש לייעוץ או קביעת דקות של האובייקטDate
. -
getHours()
|setHours()
מאפשר לך להתייעץ או לשנות את השעות (מ-0 ל-23) של האובייקטDate
. -
getDay()
מחזירה את היום בשבוע עבור התאריך, מבוטא כערך מ-0 עד 6 (ראשון עד שבת). -
getDate()
|setDate()
מחזירה או משנה את היום בחודש של האובייקטDate
שעליו הוא מוחל. -
getMonth()
|setMonth()
משמש כדי לעיין או לשנות את מספר החודש של האובייקטDate
. -
getFullYear()
|setFullYear()
שואל או מגדיר את ערך השנה באובייקט המכיל את התאריך והשעה.
השיטות הקודמות של Date
לכלול גרסה UTC להיות מסוגל לעבוד ישירות עם זמן אוניברסלי מבלי לעשות חישובי ביניים. במובן הזה, למשל, getHours()
יש גרסה getUTCHours()
o getMilliseconds()
חלופה getUTCMilliseconds()
לעבוד לחילופין עם הזמן הרשמי (החוקי) או האוניברסלי. עם getTimezoneOffset()
אתה יכול לדעת את ההבדל הקיים בין זמן אוניברסלי לזמן רשמי מקומי.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
var dia_semana=[“domingo”,“lunes”,“martes”,“miércoles”,“jueves”,“viernes”,“sábado”];
var nombre_mes=[“enero”,“febrero”,“marzo”,“abril”,“mayo”,“junio”,“julio”,“agosto”,“septiembre”,“octubre”,“noviembre”,“diciembre”];
var digitos_hora;
var hoy=new Date();
var texto_hoy=“”;
texto_hoy+=“Hoy es “;
texto_hoy+=dia_semana[hoy.getDay()];
texto_hoy+=“, “;
texto_hoy+=hoy.getDate();
texto_hoy+=” de “;
texto_hoy+=nombre_mes[hoy.getMonth()];
texto_hoy+=” de “;
texto_hoy+=hoy.getFullYear();
texto_hoy+=” y son las “;
digitos_hora=hoy.getHours();
texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora;
texto_hoy+=“:”;
digitos_hora=hoy.getMinutes();
texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora;
texto_hoy+=“:”;
digitos_hora=hoy.getSeconds();
texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora;
|
פונקציות JavaScript
אם אתה קורא את זה אתה בוודאי יודע איך לתכנת. מיקרו-בקרים en C ב C + + ולהכיר את מושג הפונקציה. למרות שהרעיון הבסיסי זהה, ב JavaScript האופן שבו הם מוגדרים והשימוש בהם הוא קצת שונה. מלכתחילה, כבר נאמר, JavaScript הוא אינו משתמש במפורש בסוגי נתונים ולכן אינך צריך לציין זאת בעת הגדרת הפונקציה. לעקוב, לא חובה שלפונקציה תהיה שם, היא יכולה להיות אנונימית. ניתן לשייך אותם למשתנה כדי להפעיל אותם, אך ייתכן גם שלא יהיה צורך בכך שכן, לפעמים, כדאי להפעיל אותם באופן מיידי, שעבורם מתווספים הסוגריים והפרמטרים לאחר הגדרת הפונקציה.
כדי להגדיר פונקציה, קידומת function
, אם רלוונטי, כתוב את השם, הארגומנטים (הפרמטרים שהועברו לפונקציה) בסוגריים, ואת הקוד שיבוצע כאשר הפונקציה תופעל בסוגריים.
1
2
3
4
5
|
function doble(numero)
{
var resultado=numero*2;
return resultado;
}
|
בהחלט, בדוגמה הקודמת לא היה צורך כלל במשתנה "תוצאה", אבל זה תירוץ טוב לזכור את היקף משתנה, שעובד כפי שאתה מצפה: המשתנה "תוצאה" קיים רק בתוך הפונקציה "כפולה". ב JavaScript ניתן להשתמש גם let
במקום var
, לתחום משתנה להקשר של בלוק קוד (מסוגר בסוגריים מסולסלים, {
y }
)
כשדיברנו על אובייקטים בסעיף הקודם, משהו בסיסי היה חסר: מאפיינים הוגדרו אך שיטות לא הוגדרו. כצפוי, שיטות אובייקט הן פונקציות, אין להם שם והם נמצאים בשימוש (מופעל) מהשם (הנכס) שהוקצה על ידי הגדרת האובייקט.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var
termostato=
{
temperatura_actual:0.0,
temperatura_frio:18.5,
temperatura_calor:22.0,
consumo:0,
ver_temperatura:
function()
{
console.log(“Temperatura actual: “+this.temperatura_actual+” °C”);
}
}
|
בדוגמה הקודמת, כבר קיימת שיטה, "view_temperature", שמציגה את הערך של המאפיין "current_temperature" דרך המסוף. זה לא מאוד שימושי, אבל זה נותן מושג שלם יותר לגבי ההגדרה של אובייקט JavaScript.
כדי לגשת לשיטות של אובייקט (פונקציות) למאפיינים שלו, השתמש this
, כמו בדוגמה הקודמת בשורה 11, בעת שימוש במאפיין "current_temperature".
גש ל-Document Object Model (DOM) עם JavaScript
מ JavaScript יש לך גישה לתוכן של דף האינטרנט שבו הוא פועל, כמו גם כמה היבטים של הדפדפן שמציג את הדף הזה, אם כי לא למשאבי המערכת. מבנה הנתונים התומך במאפיינים ובשיטות שאליהם ניתן לגשת JavaScript חלק מאובייקט החלון, במיוחד, התוכן של האובייקט (המסמך HTML) מתאים לאובייקט document
. למרות שלעיתים משתמשים בו לצורך הבהירות, אין צורך להקדים חלון לשיטות או למאפיינים כדי להתייחס אליהם, די, למשל, להשתמש document
, אין צורך לכתוב את השם של אובייקט השורש כמו ב window.document
, כל עוד יש הפניה לחלון הנוכחי.
הצורה הנפוצה ביותר של למצוא אובייקט בתוך המסמך HTML זה דרך השיטה getElementById()
, שאליו מועבר המזהה שצוין בעת יצירת הקוד כארגומנט HTML. ממה שהוסבר בסעיפים הקודמים, קל להניח שניתן לגשת גם לרכיבים בתוך האובייקט document
באמצעות תחביר נקודות (document.componente
) או בסוגריים המשתמשים בשני השם (document["componente"]
), השימושי ביותר, כגון האינדקס המספרי, קשה לשימוש ובלתי מעשי בעת גישה לתוכן של דף אינטרנט שנוצר באופן ידני.
עם JavaScript אתה יכול קבל את האלמנט שמכיל אלמנט אחר (אלמנט או צומת אב) התייעצות עם הנכס שלך parentNode
או הרכוש שלך parentElement
, ההבדל הוא שאלמנט האב (parentElement
) של האלמנט האחרון של המחרוזת DOM זה ריק (null
) וצומת האב (parentNode
) הוא המסמך עצמו (document
).
כדי לשנות את התוכן של אלמנט HTML, למשל של תווית <div>
, ניתן להשתמש בו innerHTML
וכדי לשנות את המאפיינים שלו אתה יכול לבחור להקצות לו מחלקה אחרת className
או לשנות את תכונותיו בנפרד עם style
. להתייעץ עם הסגנון שמוצג על ידי אלמנט בדף האינטרנט אינו מועיל בהכרח style
מכיוון שהוא עשוי להיות תלוי במספר גורמים או שפשוט לא צוין במפורש. כדי לבדוק את הסגנון של אלמנט המוצג לבסוף בדף האינטרנט, נעשה שימוש בשיטת getComputedStyle.
לרכיב מסמך HTML ניתן להקצות לו מספר כיתות כדי לקבוע את מראהו והתנהגותו, ל לנהל את רשימת המחלקות של אובייקט מ JavaScript אתה יכול לפנות אליו classList
שמציע את השיטות add
כדי להוסיף כיתה חדשה לרשימה, remove
להסיר אותו, toggle
כדי להחליף אותו או לעיין בתוכן של רשימת המחלקות של אלמנט איתו item
ו contains
, שמחזירה את המחלקה שתופסת מיקום מסוים ברשימה וערך true
o false
אם מחלקה מסוימת ברשימה או לא.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var contenedor_temperatura=document.getElementById(“temperatura”); // Encontrar el div con id=”temperatura”
contenedor_temperatura.innerHTML=“”; // Eliminar el contenido provisionalmente para que no se vean los cambios
contenedor.className=“bloque_temperatura”; // Asignar una nueva clase
if(temperatura>20) // Si la temperatura es mayor que 20 °C…
{
contenedor.style.color=“#FF6666”; // …usar el color rojo en lugar del color normal de la clase
}
contenedor_temperatura.innerHTML=“La temperatura es “+temperatura+” °C”; // Cuando el aspecto esté preparado mostrar el valor
if(document[“titulo”].classList.contains(“estilo_titulo”)) // Si el objeto “titulo” tiene la clase “estilo_titulo”…
{
if(document[“titulo”].classList.contains(“general”)) // …y la clase “general”…
{
document[“titulo”].classList.remove(“general”); // …quitar la clase “general”
}
}
else // Si el objeto “titulo” no tiene la clase “estilo_titulo”…
{
document[“titulo”].classList.add(“estilo_titulo”); // …añadir la clase “estilo_titulo” (da igual que tenga o no la clase “general”)
}
|
בדוגמה הקודמת הוא ממוקם עם getElementById
האובייקט שברצונך לתפעל (אלמנט <div>
עבור שלו id
), לפני שינוי המראה, התוכן נמחק על ידי הקצאה עם innerHTML
מחרוזת טקסט ריקה, היא מוקצית איתה מחלקה חדשה className
והסגנון שלו שונה עם style
בהתאם לערך התוכן (טמפרטורה), שינוי הצבע, אם ישים, דרך הנכס color
. לאחר ביסוס ההיבט, הערך נכתב באמצעות שוב innerHTML
.
בחלק השני של הדוגמה למעלה (שורות 9 עד 19) ניגשים לרכיב קוד HTML באמצעות התחביר document[]
ואת הנכס id
של האלמנט כדי לשנות את רשימת המחלקות שלו עם השיטה classList.remove()
ועם השיטהclassList.add()
, בהתבסס על תוצאה של מספר שאילתות המבוצעות בביצועים מותנים, שבהן הם משווים באמצעות classList.contains()
.
מתי זה הולך מתייחסים לאלמנט HTML כמה פעמים לאורך הקוד JavaScript, זה קצת יעיל יותר להקצות אותו למשתנה או השתמש באינדקס שלו במקום בשם שכן, אחרת, השיטה שבה היית משתמש JavaScript כדי להשיג אותו בכל פעם, יהיה צורך בחיפוש אחר השם שלו, תוך צורך במעט יותר זמן מאשר אם ניגש למשתנה.
כדי הוסף אובייקטים חדשים למסמך HTML, ניתן ליצור אותם תחילה בשיטה createElement
de document
ומאוחר יותר לשלב אותם בשאר האלמנטים בנקודת העץ שיש צורך איתו appendChild
. כדי ליצור אובייקט XML, כמו חפצים SVG שבו אנו משתמשים כדי לצייר את הגרף של חיישני ה-IoT, אתה יכול להשתמש createElementNS
(NS עבור מרחב שם). כפי שהוסבר כשמדברים על הפורמט SVG, מרחב השמות שמתאים לו (עבור הגרסה הנוכחית) הוא http://www.w3.org/2000/svg
, שאליו יש להעביר createElementNS
כארגומנט יחד עם סוג האלמנט, svg
, במקרה הזה.
א חלופי ל innerHTML
כדי להוסיף טקסט כתוכן לרכיב מסמך HTML היא השיטה createTextNode()
של האובייקט document
. עם החלופה הזו אתה יכול ליצור טקסט חדש (שאפשר לגשת אליו מאוחר יותר אם הוא מוקצה למשתנה) שמשולב בעץ האובייקטים עם השיטה appendChild()
. כאילו חלופי ל appendChild()
, שמוסיף את התוכן החדש לסוף מה שכבר קיים בצומת אליו הוא נוסף, אתה יכול להשתמש השיטה insertBefore()
, שמוסיף אובייקט חדש לפני קיים. לִלבּוֹשׁ insertBefore()
במקום appendChild()
מספק שיטה המשרתת, למשל, ל מיון אובייקטים חדשים מול אובייקטים קיימים כאשר אלמנט חייב להיות מול אחר (כמו ברשימה) או לכסות או להיות מכוסה במבנה גרפי שבו יש אלמנטים קרובים יותר לקדמה או לרקע.
הגיבו לאירועים עם JavaScript
כאשר הדרך של השתמש בדף אינטרנט כמיכל עבור גרפי חיישנים מחוברים ל-IoT זה היה משומש onload
בתווית <body>
כדי להתחיל לצייר את הגרף. מאפיין זה, המשויך לאובייקטי הקוד HTML, מתייחס ל אירועים JavaScript. כפי שכבר הוסבר, הוא מבצע פונקציה כאשר הדף נטען. למרות שזה נקשר לקוד HTML כדי לזכור את זה יותר, אפשר היה לכתוב את זה בקוד JavaScript כמו body.onload=dibujar;
siendo dibujar
שם הפונקציה שאמורה להתחיל בעת טעינת דף האינטרנט.
בגרסאות האחרונות של JavaScript ניתן לשייך אירועים לפונקציות באמצעות addEventListener
עם הפורמט objeto.addEventListener(evento,función);
או באמצעות התחביר objeto.evento=función;
שעובד גם ביישומים ישנים יותר. כדי לבטל את הקישור לפונקציה המשויכת לאירוע, יש לך removeEventListener
בעל אותו פורמט כמו addEventListener
.
JavaScript הוא מסוגל להגיב למספר רב של אירועים שיכולים להתרחש בדף אינטרנט. לדוגמה, הוא יכול לזהות מתי לוחצים על רכיב HTML עם onmousedown
, או כאשר לוחצים עם onclick
, כאשר מקש נלחץ עם onkeydown
, על ידי הפעלת פס הגלילה עם onscroll
. למטרה שלנו זה מספיק לנו לזהות טעינת עמוד עם onload
ושינוי הגודל שלו עם onresize
. נקשר את האירועים הללו לחפצים body
y window
דל DOM בהתאמה. את הראשון ניתן להקצות בקוד HTML, כפי שנראה והשני בתוך הקוד JavaScript בתוך הפונקציה שנקראת על ידי הראשון ועם הפורמט window.onresize=redimensionar;
siendo redimensionar
הפונקציה שתיקרא בכל פעם שהחלון משנה גודל.
רוץ לאחר מרווח זמן
JavaScript יש שני משאבים עבור דחיית ביצוע: setTimeout
, אשר מבצעת פונקציה לאחר מרווח זמן ו setInterval
אשר יבצע פונקציה בכל מרווח זמן מסוים. שתי השיטות דורשות כפרמטרים (1) את הפונקציה המופעלת ו-(2) את מרווח הזמן המבוטא באלפיות שניות. כדי לעצור את פעולתם, אתה יכול להקצות את התוצאה המוחזרת על ידי פונקציות אלה למשתנים ולהעביר אותם כארגומנט אל clearTimeout
או clearInterval
כאשר אתה לא רוצה להפעיל אותם שוב (או כאשר אתה לא רוצה שהם יתבצעו בפעם הראשונה) setTimeout
o setInterval
בהתאמה.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
var cuenta_atras=setTimeout(descansar,1000*60*20); // Recordar que hay que descansar cuando pasen 20 minutos
var repeticion=setInterval(consultar_correo,1000*60*5); // Revisar el correo cada 5 minutos
function descansar()
{
alert(“Puedes descansar un rato”); // Esto solamente aparecerá una vez
}
function consultar_correo()
{
alert(“Revisa el correo electrónico”); // Esto aparecerá cada cinco minutos
}
function detener_cuenta_atras() // No utiliza argumentos sino la variable global
{
clearTimeout(cuenta_atras); // Detener la cuenta atrás para avisar a los 20 minutos
}
function no_avisar_lectura_correo() // No utiliza argumentos sino la variable global
{
clearInterval(repeticion); // Dejar de avisar cada 5 minutos
}
|
בדוגמה הקודמת השיטה מוצגת alert
המשמש להצגת תמרור אזהרה. למרות שהוא היה בשימוש נרחב בעבר, הוא כמעט אסור בקוד JavaScript בגלל כמה אגרסיבי (פולשני) זה לכסות את דף האינטרנט בתיבת דו-שיח.
בתוכנית שנכתבה עבור א מיקרו-בקר של סדרה קטנה (כמו זו שעל הצלחת ארדונו אונו) מקובל להשתמש במשתנים גלובליים, כמו בדוגמה הקודמת ב JavaScript, שכן הקוד קצר ולא מבלבל במיוחד, מכיוון שפעמים רבות הפונקציות מיושמות אד-הוק ובגלל שהשימוש במשתנים גלובליים מאפשר לחזות את השימוש בזיכרון בצורה פשוטה ואינטואיטיבית מאוד, שהיא קריטית במערכות עם מעט משאבים. במקום זאת, en JavaScript מקובל לצמצם את השימוש במשתנים גלובליים למינימום האפשרי. מכיוון שהוא לא צריך להאיץ בשימוש בזיכרון, מכיוון שהוא פועל כרגיל על a CPU עם משאבים עדיפים בהרבה על אלה של א MCU, מכיוון שהוא צפוי להתקיים יחד עם הרבה קוד של צד שלישי איתו הוא חייב לעבוד מבלי להתערב ומכיוון שזו מערכת פתוחה, לא ניתן לחזות את הקשר הביצוע העתידי (התוכנית של מיקרו-בקר small קובע לחלוטין את פעולתו מבלי להוסיף עוד קוד ברגע שהוא בפעולה) ומכיוון שממדי היישומים עלולים להקשות על הקריאה אם הקוד אינו מקפל את פעולתו, מה שהופך את השיטות לעצמאיות ככל האפשר.
פעולות מתמטיות עם אובייקט JavaScript Math
הפעולות המתמטיות של חישוב מתמטי מסובך יותר מקובצות באובייקט Math
. אובייקט זה משמש ישירות, אין צורך להפעיל אותו כדי להשתמש בשיטות או במאפיינים (הקבועים) שהוא משלב.
Math.abs(n)
ערך מוחלט של פרמטר nMath.acos(n)
Arccosine של פרמטר n (תוצאה ברדיאנים)Math.asin(n)
ארקסין של פרמטר n (תוצאה ברדיאנים)Math.atan(n)
ארקטנגנט של פרמטר n (תוצאה ברדיאנים)Math.atan2(n,m)
ארקטנגנט של n/m (התוצאה ברדיאנים)Math.ceil(n)
עיגל את הפרמטר למספר השלם הקרוב ביותר כלפי מעלהMath.cos(α)
קוסינוס של פרמטר α (α ברדיאנים)Math.E
מספר e (≃2.718281828459045)Math.exp(n)
e מועלה לפרמטר n: הnMath.floor(n)
עיגל את הפרמטר n למספר השלם הקרוב למטהMath.log(n)
לוגריתם טבעי (בסיס e) של פרמטר nMath.LN2
לוגריתם טבעי (בסיס e) של 2 (≃0.6931471805599453)Math.LN10
לוגריתם טבעי (בסיס e) של 10 (≃2.302585092994046)Math.LOG2E
בסיס 2 לוגריתם של e (≃1.4426950408889634)Math.LOG10E
בסיס 10 לוגריתם של e (≃0.4342944819032518)Math.max(a,b,c,…)
הערך הגדול ביותר ברשימת הפרמטרים שעברוMath.min(a,b,c,…)
הערך הקטן ביותר ברשימת הפרמטרים שעברוMath.PI
מספר π (≃3.141592653589793)Math.pow(n,m)
פרמטר ראשון n מועלה לפרמטר השני m: nmMath.random()
מספר (כמעט) אקראי בין 0.0 ל-1.0Math.round(n)
עיגל את הפרמטר n למספר השלם הקרוב ביותרMath.sin(α)
סינוס של פרמטר α (α ברדיאנים)Math.sqrt(n)
שורש ריבועי של פרמטר nMath.SQRT1_2
שורש ריבועי של 1/2 (≃0.7071067811865476)Math.SQRT2
שורש ריבועי של 2 (≃1.4142135623730951)Math.tan(α)
טנגנט של פרמטר α (α ברדיאנים)
טען נתונים מהשרת עם AJAX
השיטה שננקטה כדי לצייר את המידע המאוחסן ב-IoT מורכבת מטעינת הנתונים מהשרת מעת לעת ושרטוט מחדש של הגרף איתו הם מיוצגים. כדי לקרוא נתונים מהשרת, נעשה שימוש בטכנולוגיה AJAX (א-סינכרוני JavaScript ו-XML) דרך חפץ XMLHttpRequest
de JavaScript. שרטוט גרף הנתונים נעשה על ידי שימוש חוזר באובייקט SVG שכבר נמצא בקוד HTML ואשר מכילה עלילה שהקואורדינטות שלה משתנות כדי שיתאימו לנתונים החדשים שנטענו.
בדוגמה של הצעה זו, בנוסף לעדכון השרטוט, מתעדכן גם טקסט בדף האינטרנט המציג את התאריך והערך של הנתונים שנמדדו אחרונים עבור כל גרף.
בצד השרת ישנו מסד נתונים המכיל את המידע שהחיישנים המחוברים ל-IoT ניטרו. מסד נתונים זה נקרא על ידי בקשת האובייקט XMLHttpRequest
להגיב עם מידע מקודד ב- פורמט JSON, למרות ששם השיטה שבה נעשה שימוש מרמז על קשר עם הפורמט XML.
במדריך הראשון של polaridad.es על אחסון נתונים של IoT ניתן לראות דוגמה לתשתית לניהול, מצד השרת, את המידע המסופק על ידי מכשירים המחוברים לאינטרנט של הדברים. בסדרת מאמרים זו שרת משמש כמשאב אַפָּשׁ שממנו תוכל להשתמש בשפת התכנות PHP כדי לגשת למסד נתונים MySQL o מריאד. בשרתים המשמשים לתמיכה ב-IoT נפוץ מאוד למצוא מסדי נתונים MongoDB (NoSQL) ושפת התכנות JavaScript על Node.js כתשתית תוכנה.
הפונקציה הבאה אחראית לבקשת הנתונים העדכניים ביותר מאחד החיישנים מהשרת. בקריאה לפונקציה, האובייקט משמש כארגומנט JavaScript שתומך בנתונים שצוירו. אם אותו גרף מייצג מספר ערכים, למשל לחיפוש חזותי אחר מתאם, ניתן להגיש בקשה לשרת להחזיר כמה בו זמנית, שיטה אופטימלית יותר בשל אופן פעולת השרת. פרוטוקול HTTP.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
function consultar_ultimo_valor_sensores(objeto_grafico)
{
var consulta=‘zona=”+objeto_grafico.sufijo_nombre;
var pagina=“ultimo_valor_sensor.php”;
var resultado;
var ajax;
if(window.XMLHttpRequest)
{
ajax=new XMLHttpRequest(); // ajax=Object.create(XMLHttpRequest);
}
else // Versiones antiguas de MS Internet Explorer
{
ajax=new ActiveXObject(“Microsoft.XMLHTTP”);
}
ajax.onreadystatechange=
function()
{
if(ajax.readyState==4&&ajax.status==200&&ajax.responseType==“json”)
{
resultado=JSON.parse(ajax.responseText);
if(resultado.fecha>objeto_grafico.fecha[objeto_grafico.fecha.length–1])
{
// Normalmente se gestionará la respuesta utilizando el objeto
redibujar_grafico(objeto_grafico,resultado);
// Si los datos son sencillos y tienen una estructura clara puede ser más práctico usarla directamente
// redibujar_grafico(objeto_grafico,[resultado.fecha,resultado.temperatura]);
}
}
}
ajax.open(“POST”,pagina);
ajax.setRequestHeader(“Method”,“POST “+pagina+” HTTP/1.1″);
ajax.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);
ajax.setRequestHeader(“Content-length”,consulta.length);
ajax.setRequestHeader(“Connection”,“close”);
ajax.send(consulta);
}
|
בשורה השלישית של הדוגמה הקודמת מכינים את השאילתה שתבוצע לשרת, בה יועבר הארגומנט "zone", שערכו יהיה השם או הקוד של המקום המנוטר שכן מידע על אזור עשוי להתקיים באותו מסד נתונים חיישנים שונים (לדוגמה, מדי חום המודדים את הטמפרטורה בחדרים שונים). הפרמטר המועבר לפונקציה הקודמת, האובייקט עם נתוני התרשים, צפוי לכלול מאפיין עם שם החדר ("שם_סיומת").
בין שורות 7 ל-14 של הקוד הקודם, ה- חפץ XMLHttpRequest
אשר מאוחסן במשתנה "ajax". לפני שאתה בוחר כיצד ליצור את האובייקט, אתה מחפש window
אם XMLHttpRequest
לא היה זמין (דבר שקרה בגרסאות ישנות של Explorer של מיקרוסופט ולמרות שהוא נמצא הרחק מאחור, הוא משמש דוגמה לחלופות ליצירת האובייקט באמצעות התחביר (המקורי יותר). Object.create
o new
, בדומה לזו של שפות מונחות עצמים אחרות.
על מנת שניתן יהיה לנהל את התגובה באופן מיידי, הקוד המטפל בה מוכן בשורות 15 עד 26 לפני ביצוע הבקשה לשרת.
הדרך של לבצע את השאילתה HTTP לשרת מורכב מ לפתוח חיבור עם open
מציין סוג ועמוד (אופציונלי שם משתמש וסיסמה), להכין את הכותרות של הפרוטוקול עם setRequestHeader
y לשלוח את הבקשה עם send
. הכותרת HTTP Content-length
תצטרך לדעת את אורך השאילתה (מספר תווים) אשר מחושב באמצעות length
.
כאשר הבקשה AJAX מוכן, הפונקציה הקשורה לאירוע מבוצעת onreadystatechange
. במקום להקצות פונקציה, בדוגמה הקודמת מוגדרת תוך כדי תנועה פונקציה אנונימית שתנהל את קליטת הנתונים המגיעים מהשרת. קודם כל בשורה 18 מוודא שסטטוס הבקשה "סתיים", התואם את הערך 4
של הנכס readyState
, שהסטטוס הוא "בסדר" של ה פרוטוקול HTTP (קוד 200
) שניתן להשיג מהנכס status
וכי הנתונים שהגיעו הם פורמט JSON, התייעצות עם הנכס responseType
.
לאחר אימות שסטטוס התגובה הוא כצפוי, בשורה 20 של הדוגמה הקודמת יוצר אובייקט עם התוצאה, וממיר את הטקסט JSON. התגובה מספקת תאריך להחזרה, זה מאפשר לנו לראות אם התוצאה שהשרת שולח כבר מיוצגה בעבר בגרף, שמאומת בשורה 21. אם הנתונים חדשים, בשורה 23 הפונקציה ש אחראי לשרטוט מחדש של הגרף עם המידע החדש שנקרא.
הרעיון כאשר מציעים שיטת קריאה זו הוא שהנתונים יתרעננו בתדירות גבוהה מאוד. אם המידע המוצג מתאים לטווח ארוך (כגון טמפרטורות של יום או שבוע), ניתן ליישם בקשה ראשונית שאוספת את כל הנתונים הזמינים ולאחר מכן אחת, בדומה לזה שבדוגמה, שמעדכנת אותם בתוך כתב התקופה.
צור נתונים אקראיים לבדיקה
כאשר כל תשתית השרת והלקוח מוכנים, פונקציה כמו זו בסעיף הקודם תהיה אחראית על קריאת הנתונים ושרטוט הגרף איתם, אבל בשלב הבדיקה זה עשוי להיות מעשי יותר להשתמש במספרים אקראיים בטווח מבוקר כדי לראות אם הקוד הנכתב נכון. הפונקציה הבאה יכולה לשמש דוגמה להשגת נתונים בזמן בניית האפליקציה הסופית.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
function consultar_ultimo_valor_sensores
(
objeto_grafico,// Objeto dentro del elemento SVG que representa el trazado
valor_maximo, // Valor máximo
valor_minimo, // Valor mínimo
margen_valor // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
)
{
// La forma más genérica es usar objetos
/*
var nuevo_valor_inventado=
{
fecha:Math.round(Date.now()+Math.random()*1000),
temperatura:Math.random()*Math.abs(valor_maximo-valor_minimo)+valor_minimo
};
*/
// En este caso concreto, como son pocos datos y con una estructura concreta es más práctico usar un vector
var nuevo_valor_inventado=
[
Math.round(Date.now()+Math.random()*1000),
Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo
];
redibujar_grafico(objeto_grafico,nuevo_valor_inventado);
}
|
במקום לקרוא את המידע ממסד נתונים, הדוגמה למעלה מייצרת אותם באופן אקראי ומעבירה אותם לפונקציה שאחראית על שרטוט הגרף. הנתונים המומצאים הם וקטור שנוצר על ידי תאריך המבוטא כערך באלפיות שניות, רגע הקלטת מידע החיישן והנתונים המנוטרים, שנמצאים בין ערך מקסימלי לערך מינימלי.
בדוגמה זו, בעת הפקת תאריך ניתן לעכב אותו עד שנייה אחת (1000 מילישניות) ביחס לתאריך בזמן ההמצאה. כפי ש Math.random()
יוצר מספר בין 0.0 ל-1.0, הכפלתו ב-1000 מייצרת מספר בין 0 ל-1000 אשר לאחר מכן מומר למספר שלם. באותו אופן, הערך מתקבל על ידי הכפלת המספר האקראי בטווח (מקסימום מינוס מינימום) והוספת המינימום.
צייר את גרף חיישני ה-IoT עם עלילת SVG
מכיוון שראינו כיצד אנו יכולים להשיג את הערכים שאנו רוצים לייצג (טמפרטורה, בדוגמה) ואת מיקומם הזמני, שניתן לבטא יחד בצורה של קואורדינטות, הדוגמה למטה מציגה פונקציה לשרטוט נתיב שמצטרף לאותן נקודות ובאופן אופציונלי אזור צבעוני התחום על ידי הקו הזה בחלק העליון. התוצאה תהיה כמו התמונה הבאה.
הציר האופקי (X) של הגרף מייצג את הזמן והציר האנכי (Y) את הערכים שהחיישנים המחוברים ל-IoT ניטרו. המרווח האופקי הוא כמה שניות שכן בהצעה זו הגרף מתעדכן בתדירות גבוהה (כל שנייה, למשל) כדי לספק מידע כמעט בזמן אמת על מצב החיישנים.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
function actualizar_grafico
(
grafico, // Objeto SVG con el que se dibuja la gráfico
coordenada, // Matriz con las coordenadas del trazado formada por pares [tiempo,valor] ordenados primero el más antiguo (tiempo menor) último el más nuevo
tiempo_total_representado, // Tiempo total representado por la gráfico (en milisegundos)
valor_maximo, // Valor máximo aceptable antes de emitir una alarma
valor_minimo, // Valor mínimo aceptable antes de emitir una alarma
margen_valor, // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
parametro_cerrado, // Valor booleano que indica si el trazado se cierra o no (por defecto false)
parametro_ancho_caja, // Ancho de la caja que contiene la gráfico (por defecto 100.0)
parametro_alto_caja // Alto de la caja que contiene la gráfico (por defecto 100.0)
)
{
var cerrado=parametro_cerrado||false; // Valor booleano que indica si el trazado se cierra o no (por defecto false)
var ancho_caja=parametro_ancho_caja||100.0; // Ancho de la caja que contiene la gráfico (por defecto 100.0)
var alto_caja=parametro_alto_caja||100.0; // Alto de la caja que contiene la gráfico (por defecto 100.0)
var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG
var desplazamiento=[]; // Desplazamientos X e Y para posicionar el tiempo y el valor representado dentro del rango del gráfico
var escala=[]; // Coeficientes X e Y para calcular el tamaño al representar el gráfico
var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos)
var contador_valor=coordenada.length–1; // Variable para recorrer los valores (índice)
var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado
escala[0]=ancho_caja/tiempo_total_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
escala[1]=alto_caja/(Math.abs(valor_maximo–valor_minimo)+margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
desplazamiento[0]=coordenada[coordenada.length–1][0]–tiempo_total_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
desplazamiento[1]=margen_valor–valor_minimo; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
if(cerrado) // Si se dibuja un path (trazado) cerrado…
{
coordenadas_trazado+=ancho_caja+“,”+alto_caja+” L “; // …se empieza por la parte inferior de la caja
}
while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico…
{
posicion[0]=(coordenada[contador_valor][0]–desplazamiento[0])*escala[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal
posicion[1]=alto_caja–(coordenada[contador_valor][1]+desplazamiento[1])*escala[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG)
coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y
if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo…
{
coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que represntar, añadir una nueva línea (código L) para el próximo
contador_valor—; // Pasar al siguiente valor
}
else // Si se ha rebasado el margen izquierdo…
{
sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas)
}
}
if(cerrado) // Si se dibuja un trazado (path) cerrado…
{
coordenadas_trazado+=” L “+posicion[0]+“,”+alto_caja+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG
}
grafico.setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado
}
|
בקוד הקודם יש שני היבטים מעניינים, ראשית החישוב שמאפשר להתאים את טווח הערכים המיוצגים ושנית את בניית נכסים d
מה שמציין את הקואורדינטות של הנקודות בפריסה (path
).
כדי להתאים את טווח הערכים המיוצגים, הם מוזזים ממינימום ומותאמים כך שהגודל הנראה מתאים לגודל הגרף. במקרה של זמן, ההיסט מתקבל על ידי הפחתת הטווח שברצונך להציג מהזמן הארוך ביותר (התאריך והשעה הקרובים ביותר לעכשיו) (20 שניות בדוגמה). העקירה של ערכי הטמפרטורה היא זו של הטווח התחתון (מעלה אחת) פחות הערך הנמוך ביותר, כך שהנתונים המוצגים להלן דומים ביותר לערך הנמוך ביותר המותר אך משאירים מרווח המאפשר לנו להעריך מה עובר
המקדם שמכפיל את ערכי הזמן כדי לקבל את הקואורדינטות האופקיות של הגרף מתקבל על ידי חלוקת הרוחב הכולל של הגרף (100 יחידות בדוגמה) בטווח הזמן המיוצג (20 שניות בדוגמה). כדי לקבל את המקדם עם ערכי הטמפרטורה הסקלרית, יש לזכור שהטווח המיוצג עובר משוליים מתחת לערך המינימלי למרווח מעל המקסימום, מעלה אחת בשני המקרים. בדרך זו, מקדם הסולם האנכי נובע מחלוקת גובה הגרף (100 יחידות בדוגמה) בערך המקסימלי, פחות המינימום בתוספת השוליים העליונים והתחתונים. מכיוון שערכים אלה יכולים להתפתח לחלוטין בטמפרטורות שליליות, אנו משתמשים Math.abs()
להשתמש בערך המוחלט של ההפרש.
הרכוש d
של האובייקט path
הוא נבנה על ידי שרשור הקואורדינטות של הנקודות בטקסט. לפני כל זוג קואורדינטות יש קוד SVG L
, המצייר קו מהמיקום הנוכחי לערך מוחלט המסומן על ידי הקואורדינטות. ערכי X ו-Y מופרדים על ידי פסיקים וכל פעולה SVG מופרד על ידי רווח מהבא.
כדי להתחיל את הפריסה, השתמש בקוד M
(עבור לקואורדינטה מוחלטת). במקרה של החלקה הסגורה והמלאה מתחילים מימין למטה, במקרה של החלקה הפתוחה שמציירת את פרופיל הנתונים מתחילים בערך האחרון המיוצג (האחרון ביותר). כדי לסיים את הפריסה הסגורה, נעשה שימוש בקוד Z
הוספת כנקודה האחרונה את זו בעלת אותו ערך קואורדינטת X כמו הנקודה האחרונה של הקו וכקואורדינטת Y את הערך הקטן ביותר המיוצג.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
function dibujar_grafico()
{
var tiempo_mostrado=20000; // Se representan 20 segundos (20000 milisegundos)
var valor_maximo=10; // 10 grados sobre cero
var valor_minimo=–5; // Cinco grados bajo cero
var fecha_hora=Date.now(); // Hora actual
var matriz_de_coordenadas_de_prueba=[]; // Preparar el vector de coordenadas
for(var contador=0;contador<20;contador++)
{
fecha_hora+=500+1500*Math.random(); // Añadir medio segundo a la hora anterior y entre 0 y segundo y medio aleatoriamente
matriz_de_coordenadas_de_prueba[contador]=[]; // Preparar el siguiente punto del vector de coordenadas
matriz_de_coordenadas_de_prueba[contador][0]=fecha_hora; // En la coordenada horizontal, situar la hora
matriz_de_coordenadas_de_prueba[contador][1]=Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo; // En la coordenada vertical situar el valor del sensor
}
actualizar_grafico
(
document.getElementById(“relleno_temperatura”), // Trazado para el relleno definido en el código HTML
matriz_de_coordenadas_de_prueba,
tiempo_mostrado,
valor_maximo, // Diez grados de valor máximo
valor_minimo, // Cinco grados bajo cero como valor mínimo
1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente
true // Cerrar el trazado para representar el área rellena
);
actualizar_grafico
(
document.getElementById(“linea_temperatura”), // Trazado para el relleno definido en el código HTML
matriz_de_coordenadas_de_prueba,
tiempo_mostrado,
valor_maximo, // Diez grados de valor máximo
valor_minimo, // Cinco grados bajo cero como valor mínimo
1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente
false // No cerrar el trazado para representar la linea que une los puntos que representan las temperaturas
);
}
|
בדוגמה זו, הפונקציה dibujar_grafico()
, שהיא הקריאה לטעינת עמוד, מקבל את הערכים הראשוניים לבדיקה (לא הערך האחרון בזמן אמת) ומכין את הטווח שבו הנתונים יוצגו: 20 שניות (20000 אלפיות השנייה) אופקית ו-15°C ב אנכי מ-5°C עד +10°C עם מעלה אחת עליון ותחתון. בצע שתי שיחות ל actualizar_grafico()
, במעבר הראשון true
כארגומנט, המציין שצריך לסגור את התרשים כדי לייצג אזור מלא, ובקריאה השנייה הוא עובר false
למתוח את הקו. בכל מקרה, האובייקט path
שונה הוא זה שיש לו את המראה המקביל, עם מילוי וללא גבול במקרה הראשון ועם עובי קו מסוים וללא מילוי במקרה השני.
הפונקציה actualizar_grafico()
לעבוד על חפץ SVG שמשתמש בקוד הבא בתור מיכל HTML. האובייקט SVG מכיל שני נתיבים, אחד לציור הקו ואחר לציור השטח המלא. בעת טעינת דף האינטרנט, מהאלמנט <body>
הפונקציה הקודמת נקראת אוטומטית, dibujar_grafico()
בזכות האירוע JavaScript onload
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!DOCTYPE html>
<html lang=“es”>
<head>
<meta charset=“utf-8”>
<title>Temperatura</title>
<script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/grafico.js”></script>
</head>
<body onload=“dibujar_grafico();” style=“margin:0;”> <!– Cuerpo del documento HTML. Al cargar el contenido llama a la función JavaScript dibujar_grafico() –>
<div id=“temperatura”>
<div id=“bloque_temperatura” style=“width:820px;height:150px”>
<svg
id=“grafico_temperatura”
width=“100%”
height=“100%”
viewBox=“0 0 100 100”
preserveAspectRatio=“none”>
<path
id=“relleno_temperatura”
d=“”
style=“fill:#A8C3EA;stroke:none;”
vector-effect=“non-scaling-stroke”
/>
<path
id=“linea_temperatura”
d=“”
style=“fill:none;stroke:#205587;stroke-width:4;stroke-opacity:1;”
vector-effect=“non-scaling-stroke”
/>
</svg>
</div>
</div>
</body>
</html>
|
בשורה 10 של הקוד HTML למעלה, נקבע בסגנון רוחב (כדוגמה) של 820 פיקסלים וגובה של 150 פיקסלים (דבר שבגרסה הסופית, יהיה מומלץ לעשות עם מחלקה ומסמך CSS). זה נראה מוזר ששורות 13 ו-14 מגדירות את גודל האובייקט SVG כמו 100% רוחב וגובה (המתאימים ביותר למידות החלון, 100×100). כפי שכבר צוין, הסיבה לעשות זאת היא לעבוד תמיד עם ממדים ידועים ולהתאים את הערכים המיוצגים אליו. האלטרנטיבות האחרות יהיו לחשב את המרחב של הגרף בכל פעם ולאחר מכן להתאים מחדש את הערכים או להפעיל מידות קבועות עבור הגרף, שהמסמך יצטרך לדבוק בהם.
לאחר שבחרת בגרף שמידותיו משתנות בהתאם לקוד HTML, יש צורך לכלול את הנכס vector-effect
עם האומץ non-scaling-stroke
למנוע עיוות של עובי קו כאשר הגרף אינו שומר על הפרופורציות הנבחרות של 1:1 בדף האינטרנט בו הוא מוצג, כפי שקורה בהצעה הקודמת.
כדי "לחתוך" את הגרף ולהראות רק את האזור שתבחר, השתמש viewBox
. במקרה זה בחרנו לראות את החלק של הגרף שמתחיל ב-0,0 (פינה השמאלית העליונה) ובגודל 100x100 למטה וימינה. החלק של הציור הממוקם בקואורדינטות עם ערכים שליליים או גדולים מ-100 לא יוצג בדף האינטרנט גם אם הם קיימים באובייקט SVG
הוסף אלמנטים חדשים לשרטוט SVG
בדוגמה הקודמת, הפונקציה actualizar_grafico()
להשתמש בפריסה SVG שאליו מתחלפת הבעלות d
, שזה מה שמבטא את שרשרת הקואורדינטות. האלטרנטיבה תהיה ליצור את האובייקט כולו בכל פעם שהוא מצויר מחדש. היתרון של האפשרות הראשונה הוא שהמראה הגרפי (כגון עובי או צבע) מוגדר בקוד HTML, המגבלה היא שהאובייקטים חייבים להיווצר קודם לכן.
כדי ליצור אובייקטי SVG, השתמש createElementNS()
, המאפשר לכלול את מרחב שמות. בדוגמה למטה נוצר אובייקט טקסט חדש (text
) והוא משויך לאלמנט SVG שכבר קיים בקוד HTML של האתר. לאחר יצירת האלמנט החדש, המאפיינים שלו מוקצים עם setAttribute()
ומתווסף אליו SVG עם appendChild()
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function
rotular
(
objeto_grafico,
texto,
inicio=[0,0],
altura=10.0,
tipo_letra=“SircuitoRegularMedium”,
color_texto=“#000000”,
color_fondo=“#FFFFFF”
)
{
nuevo_objeto_svg=document.createElementNS(“http://www.w3.org/2000/svg”,“text”);
nuevo_objeto_svg.setAttribute(“x”,inicio[0]);
nuevo_objeto_svg.setAttribute(“y”,inicio[1]);
nuevo_objeto_svg.setAttribute(“font-family”,tipo_letra);
nuevo_objeto_svg.setAttribute(“font-size”,altura);
nuevo_objeto_svg.setAttribute(“fill”,color_texto);
nuevo_objeto_svg.textContent=texto;
objeto_grafico.appendChild(nuevo_objeto_svg);
}
//rotular(document.getElementById(“cosa_svg”),”HOLA”,[10,10]);
|
שנה את הפרופורציה של רכיבי ציור
אם ניסיתם לתייג עם הפונקציה בדוגמה בסעיף הקודם, תראו שהטקסט נראה מעוות כאשר הפרופורציה של האובייקט בדף האינטרנט (width
y height
של קוד HTML) אינו שווה לזה של השטח המיוצג (viewBox
). כדי להתאים את הפרופורציה יש צורך לדעת את המידות של האובייקט SVG עבורו ניתן להתייעץ בסגנון האובייקט, או המיכל HTML, אם החפץ SVG להעביר את הנכס הזה. הקצאת בעלות transform
לחפצים SVG התלויים בפרופורציה, ניתן לתקן את העיוות על ידי הפעלת פעולת קנה מידה scale()
שבו המקדם ב-X שונה מזה שב-Y.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
function
rotular
(
objeto_grafico,
texto,
inicio=[0,0],
altura=10.0,
proporcion=1.0,
tipo_letra=“SircuitoRegularMedium”,
color_texto=“#000000”,
color_fondo=“#FFFFFF”
)
{
var escala_horizontal=parseFloat(getComputedStyle(objeto_grafico).height)/parseFloat(getComputedStyle(objeto_grafico).width);
nuevo_objeto_svg=document.createElementNS(“http://www.w3.org/2000/svg”,‘text’);
nuevo_objeto_svg.setAttribute(“transform”,“scale(“+escala_horizontal+“,1.0)”); // scale permite cambiar la escala en X e Y
//nuevo_objeto_svg.setAttribute(“transform”,”scaleX(“+escala_horizontal+”)”); // Como se sabe que sólo cambia la escala en X, se puede usar scaleX
nuevo_objeto_svg.setAttribute(“x”,inicio[0]);
nuevo_objeto_svg.setAttribute(“y”,inicio[1]);
nuevo_objeto_svg.setAttribute(“font-family”,tipo_letra);
nuevo_objeto_svg.setAttribute(“font-size”,altura);
nuevo_objeto_svg.setAttribute(“fill”,color_texto);
nuevo_objeto_svg.textContent=texto;
objeto_grafico.appendChild(nuevo_objeto_svg);
}
//rotular(document.getElementById(“cosa_svg”),”HOLA”,[10,10]);
|
SVG מאפשר לקבץ מספר אובייקטים ויוצרים אלמנט מורכב חדש שתומך גם במאפיינים, כמו חפצים פשוטים. כדי להחיל את אותה טרנספורמציה על סדרה של אובייקטים בבת אחת במקום כל אובייקט בנפרד, ניתן לקבץ אותם לפי משאב זה ולהחיל מאפיין יחיד transform
לכולן.
כפי שהוסבר כשמדברים על פורמט SVG, הרכיבים של קבוצה סגורים בתוך התוויות <g>
y </g>
. להוסיף מ JavaScript אלמנטים לקבוצה SVG משמש, כפי שניתן לראות בדוגמה הקודמת, appendChild()
לאחר הגדרת האובייקט החדש.
כדי לקבוע מקור בעת החלת טרנספורמציות, ניתן להשתמש במאפיין על אובייקטים SVG transform-origin
, שהערך שלו הוא קואורדינטות X ו-Y של הנקודה שממנה מתחילה הטרנספורמציה. אם לא מצוין במפורש ערך למקור הטרנספורמציה (בדפדפן האינטרנט), נעשה שימוש במרכז הקואורדינטות. למרבה הצער, בזמן כתיבת שורות אלה, ציון ההתנהגות של טרנספורמציות באמצעות מקור שאינו ברירת המחדל אינו הומוגני בין הדפדפנים ויש להשתמש בזהירות.
יחד עם השינוי בקנה מידה עם scale
יש אחרים, כגון סיבוב עם rotation
והתנועה עם translate
, אשר מציעים א חלופה לייצוג גרף: במקום להשיג קואורדינטות חדשות, אתה יכול לייצג אותן במרחב שלהן ולשנות את הגרף כך שיתאים לפורמט שבו ברצונך לייצג אותן.
הוסף הפניות לתרשים
כעת, לאחר שהחלק העיקרי של הגרף נפתר על ידי התוויית הערכים עם פרופיל ושטח מלא, ניתן להשלים אותו עם הפניות המסייעות בקריאה שלו. כדוגמה, נתחיל בציור כמה הפניות אופקיות (קווים) המסמנות את הערכים המקסימליים והמינימליים המקובלים כמו גם ערך רצוי. כפי שהוסבר, אתה יכול לבחור להוסיף את האובייקטים ל- SVG היישר מ JavaScript או לכלול אותם ידנית בקוד HTML ולשנות אותם מאוחר יותר עם JavaScript.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
var CONTENEDOR_SVG; // Objeto SVG que contiene el gráfico que representa los valores monitorizados por los sensores en la IoT
var NS=“http://www.w3.org/2000/svg”; // Nombre del espacio de nombres (name space NS)
var ANCHO_CAJA=100.0; // Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var ALTO_CAJA=100.0; // Alto del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var VALOR_MAXIMO=10.0; // Mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_MINIMO=–5.0; // Menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_DESEADO=5.0; // Mejor valor del parámetro medido (temperatura). Sirve para tener una idea rápida de cómo de correcto es el estado del sistema
var MARGEN_VALOR=1.0; // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo
function inicializar_grafico()
{
CONTENEDOR_SVG=document.getElementById(“contenedor_svg”);
crear_referencia_horizontal_svg(VALOR_MAXIMO,“#FF0000”);
crear_referencia_horizontal_svg(VALOR_DESEADO,“#00FF00”);
crear_referencia_horizontal_svg(VALOR_MINIMO,“#0000FF”);
}
function crear_referencia_horizontal_svg
(
altura=0.0,
color=“#000000”,
grosor=0.5,
opacidad=1.0
)
{
var altura_corregida=ALTO_CAJA–(altura+MARGEN_VALOR–VALOR_MINIMO)*ALTO_CAJA/(Math.abs(VALOR_MAXIMO–VALOR_MINIMO)+MARGEN_VALOR*2);
var referencia_horizontal=document.createElementNS(NS,‘line’);
referencia_horizontal.setAttribute(“x1”,0.0);
referencia_horizontal.setAttribute(“x2”,ANCHO_CAJA);
referencia_horizontal.setAttribute(“y1”,altura_corregida);
referencia_horizontal.setAttribute(“y2”,altura_corregida);
referencia_horizontal.style.stroke=color;
referencia_horizontal.style.strokeWidth=grosor;
referencia_horizontal.style.strokeOpacity=opacidad;
CONTENEDOR_SVG.appendChild(referencia_horizontal);
}
//inicializar_grafico();
|
נראה הגיוני לתייג את ההפניות האופקיות הללו בטקסט שמבהיר את הערך שהם מייצגים. להדגשת הטקסט ניתן להשתמש במלבנים שיבלטו מהרקע ומהגרפיקה. מכיוון שיהיה צורך לשנות את קנה המידה של הטקסטים כדי לפצות על העיוות, ניתן לקבץ את כולם לאובייקט שעליו יוחל הסולם; היתרון העיקרי לעשות זאת בדרך זו הוא היכולת לשנות אותם בפעולה אחת אם מיכל הגרף (חלון הדפדפן) משתנה ומשנה את הפרופורציה שהקנה מידה מתקן.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
// Este código de ejemplo usa constantes para hacerlo más legible a desarrolladores de aplicaciones para microcontroladores de series pequeñas. La opción más recomendable, y más propia del estilo JavaScript, es crear un objeto cuyas propiedades serían las constantes y que incluiría los métodos (aquí funciones) que generan o modifican el gráfico o en este caso las referencias
var CONTENEDOR_SVG; // Objeto SVG que contiene el gráfico que representa los valores monitorizados por los sensores en la IoT
var NS=“http://www.w3.org/2000/svg”; // Nombre del espacio de nombres (name space NS)
var ANCHO_CAJA=100.0; // Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var ALTO_CAJA=100.0; // Alto del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var TIEMPO_REPRESENTADO=30000; // Milisegundos visibles en el gráfico empezando en el valor mayor (fecha y hora del último valor monitorizado)
var VALOR_MAXIMO=10.0; // Mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_MINIMO=–5.0; // Menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_OPTIMO=5.0; // Mejor valor del parámetro medido (temperatura). Sirve para tener una idea rápida de cómo de correcto es el estado del sistema
var MARGEN_VALOR=2.0; // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo
var desplazamiento_valor=desplazamiento(Date.now());
var escala_valor=escala();
var TIPOGRAFIA=“SircuitoRegularMedium”; // Tipografía con la que se rotula todo el gráfico (se usa una constante buscando la uniformidad, pero se puede rotular usando diferentes tipos de letra si es necesario)
var ALTURA_TEXTO=10.0; // Altura de los textos en valor absoluto (píxeles)
var COLOR_MINIMO=“#621D87”; // Color de la referencia que indica el valor mínimo
var COLOR_OPTIMO=“#1D8762”; // Color de la referencia que indica el valor máximo
var COLOR_MAXIMO=“#871E35”; // Color de la referencia que indica el valor óptimo
var COLOR_TIPOGRAFIA=“#A8C3EA”; // Color del tipo de letra con que se rotulan las referencias
var GROSOR_REFERENCIA=0.5; // Grosor de la línea que se dibuja como referencia en valor absoluto (píxeles)
var OPACIDAD_REFERENCIA=1.0; // Opacidad de la línea de referencia. Si se dibuja sobre el gráfico con cierta transparencia permite ver el dibujo bajo ella
var RELLENO_FONDO_REFERENCIA=4.0; // Margen entre el fondo de la referencia y el texto medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
var MARGEN_FONDO_REFERENCIA=10.0; // Separación de la referencia y el borde izquierdo del gráfico medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
var ANCHO_FONDO_REFERENCIA=34.0; // Medida horizontal del rectángulo que hace de fondo al texto de la referencia medido en valor absoluto
function proporcion_grafico()
{
return parseFloat(getComputedStyle(CONTENEDOR_SVG).height)/parseFloat(getComputedStyle(CONTENEDOR_SVG).width); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño
}
function medida_grafico()
{
var proporcion=[]; // Coeficiente que calcula la medida absoluta en píxeles en función del ancho/alto base del gráfico y de la representación en la página web // Coeficiente que calcula la medida absoluta en píxeles en función del ancho/alto base del gráfico y de la representación en la página web
proporcion[0]=ANCHO_CAJA/parseFloat(getComputedStyle(CONTENEDOR_SVG).width);
proporcion[1]=ALTO_CAJA/parseFloat(getComputedStyle(CONTENEDOR_SVG).height);
return proporcion;
}
function desplazamiento(valor_mayor)
{
var desplazamiento_valor=[];
desplazamiento_valor[0]=valor_mayor–TIEMPO_REPRESENTADO; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
desplazamiento_valor[1]=MARGEN_VALOR–VALOR_MINIMO; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
return desplazamiento_valor;
}
function escala()
{
var escala_valor=[];
escala_valor[0]=ANCHO_CAJA/TIEMPO_REPRESENTADO; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
escala_valor[1]=ALTO_CAJA/(Math.abs(VALOR_MAXIMO–VALOR_MINIMO)+MARGEN_VALOR*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
return escala_valor;
}
function crear_referencia_horizontal_svg
(
posicion=0.0,
color_dibujo=“#000000”,
grosor_linea=GROSOR_REFERENCIA,
opacidad_linea=OPACIDAD_REFERENCIA,
color_texto=COLOR_TIPOGRAFIA,
altura_texto=ALTURA_TEXTO
)
{
var proporcion_horizontal=proporcion_grafico(); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño
var coeficiente_medida=medida_grafico();
var posicion_corregida=ALTO_CAJA–(posicion+desplazamiento_valor[1])*escala_valor[1];
var referencia_horizontal=document.createElementNS(NS,‘line’); // El orden en el que se crean los objetos determina qué tapa (lo último) y que es tapado (lo primero)
var fondo_referencia=document.createElementNS(NS,‘rect’); // El rectángulo (opaco) se creará después de la línea (que puede ser un poco transparente) para definir con claridad el fondo del texto
var texto_referencia=document.createElementNS(NS,‘text’); // El texto se creará en último lugar para que quede sobre los otros objetos
referencia_horizontal.setAttribute(“x1”,0.0);
referencia_horizontal.setAttribute(“x2”,ANCHO_CAJA);
referencia_horizontal.setAttribute(“y1”,posicion_corregida);
referencia_horizontal.setAttribute(“y2”,posicion_corregida);
referencia_horizontal.style.stroke=color_dibujo;
referencia_horizontal.style.strokeWidth=grosor_linea*coeficiente_medida[1];
referencia_horizontal.style.strokeOpacity=opacidad_linea;
CONTENEDOR_SVG.appendChild(referencia_horizontal); // Añadir la línea de referencia lo más abajo
fondo_referencia.setAttribute(“x”,MARGEN_FONDO_REFERENCIA*coeficiente_medida[0]);
fondo_referencia.setAttribute(“y”,posicion_corregida–(ALTURA_TEXTO+RELLENO_FONDO_REFERENCIA*2.0)*coeficiente_medida[1]/2.0);
fondo_referencia.setAttribute(“width”,ANCHO_FONDO_REFERENCIA*coeficiente_medida[0]);
fondo_referencia.setAttribute(“height”,(ALTURA_TEXTO+RELLENO_FONDO_REFERENCIA*2.0)*coeficiente_medida[1]);
fondo_referencia.style.fill=color_dibujo;
fondo_referencia.style.fillOpacity=1.0;
fondo_referencia.style.strokeWidth=0;
CONTENEDOR_SVG.appendChild(fondo_referencia);
texto_referencia.setAttribute(“x”,(MARGEN_FONDO_REFERENCIA+RELLENO_FONDO_REFERENCIA)*coeficiente_medida[0]/proporcion_horizontal);
texto_referencia.setAttribute(“y”,posicion_corregida+ALTURA_TEXTO/2.0*coeficiente_medida[1]);
texto_referencia.setAttribute(“font-family”,TIPOGRAFIA);
texto_referencia.setAttribute(“font-size”,ALTURA_TEXTO*coeficiente_medida[1]);
texto_referencia.setAttribute(“fill”,COLOR_TIPOGRAFIA);
texto_referencia.setAttribute(“transform”,“scale(“+proporcion_horizontal+“,1.0)”);
texto_referencia.textContent=(posicion>=0?“+”:“”)+posicion;
CONTENEDOR_SVG.appendChild(texto_referencia);
}
function inicializar_grafico()
{
CONTENEDOR_SVG=document.getElementById(“contenedor_svg”);
crear_referencia_horizontal_svg(VALOR_MAXIMO,COLOR_MAXIMO);
crear_referencia_horizontal_svg(VALOR_OPTIMO,COLOR_OPTIMO);
crear_referencia_horizontal_svg(VALOR_MINIMO,COLOR_MINIMO);
}
//inicializar_grafico();
|
ישנם מספר היבטים מעניינים בקוד הדוגמה לעיל. קודם כל, הערה כי נעשה שימוש בקבועים (משתנים גלובליים) כדי להפוך את הדוגמה לקריאה יותר למשתמשים המגיעים מתכנות. מיקרו-בקרים en C ב C + +. כפי שנראה בהמשך, הדרך האופטימלית לתכנת אותו JavaScript זה יהיה שימוש באובייקטים שיכילו את הערכים והשיטות האלה שינהלו את ההפניות בדוגמה זו או את הגרף, באופן כללי, במערכת ייצור.
מצד שני, בקידום מה שהקוד הגנרי יותר יהיה, פותחו פונקציות נפרדות שמחשבות את המקדמים השונים שמתקנים את הפרופורציה של הגרף כדי להתאים את הטקסט proporcion_grafico()
, קנה המידה של הערכים בהתאם לטווח שלהם escala()
ומקדם תיקון למדידות הידועות בערך המוחלט, כגון מדידות בהפניות medida_grafico()
.
קריאת קוד זה אמורה לעזור להבהיר את ההקשר בו פועלת אפליקציה כמו זו, אשר מצייר גרפיקה בזמן אמת וחייבת להיות גמישה להצגה בהקשרים גרפיים שונים (בגדלים ופרופורציות שונות, לכל הפחות). קודם כל, יש ליצור את האובייקטים SVG, או "ידנית" בקוד HTML, או באמצעות קוד JavaScript ובכל מקרה, יש לקבל אזכורים לאובייקטים אלה לאחר מכן כדי לתמרן מהם JavaScript כך שניתן לצייר גרפים חדשים ולהתאים את הייצוג של גרף שצויר כבר לשינוי במדיום בו הוא מוצג.
התייחסות נוספת שיכולה לעזור לפרש בקלות גרף הן הנקודות המייצגות ערכים ספציפיים (הצמתים של הקו). בדוגמה זו, שבה אנו מייצגים גודל בודד, הבחירה בסמל אינה קריטית, אך אם מספר ערכים שונים משולבים על מנת לחפש מתאם, מעניין להבחין, בנוסף לשימוש במשאבים אחרים כגון צבע , על ידי ציור סמלים שונים. יש לשנות את הגרפיקה המשמשת לצומת הקו בגודל ובפרופורציה, כפי שקורה, למשל, עם טקסטים, כך שמידותיה יהיו מוחלטות וכדי שהפרופורציות שלה ישמרו גם אם אלו של התיבה שהיא מכילה ישנו את הגרפיקה.
בדוגמה הקודמת כבר ראינו כיצד לחשב את המקדמים השונים כדי לשנות קנה מידה ולתקן את הפרופורציה של הציור; לגבי אופן יישום ניהול הסמלים של הצמתים או קודקודי הגרף, פתרון אפשרי עשוי להיות אחסון האובייקטים SVG לתוך וקטור ולשנות את מיקומו כאשר הגרף מתעדכן על ידי קריאת ערך חדש, או כאשר הוא מצויר מחדש על ידי שינוי גודל המיכל. במקרה הראשון יהיה צורך לשנות את מיקומו ובמקרה השני היחס שלו עם הנכס transform
והערך של scale
. הקוד הבא הוא שינוי של הפונקציה actualizar_grafico()
לכלול מיקום מחדש של סמלי קודקוד הגרף.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
function actualizar_grafico_puntos
(
grafico, // Objeto SVG con el que se dibuja la gráfico
coordenada, // Matriz con las coordenadas del trazado formada por pares [tiempo,valor] ordenados primero el más antiguo (tiempo menor) último el más nuevo
puntos, // Matriz con los objetos SVG que se representan en los nodos de la línea (los valores reales)
tiempo_total_representado, // Tiempo total representado por la gráfico (en milisegundos)
valor_maximo, // Valor máximo aceptable antes de emitir una alarma
valor_minimo, // Valor mínimo aceptable antes de emitir una alarma
margen_valor, // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
parametro_cerrado, // Valor booleano que indica si el trazado se cierra o no (por defecto false)
parametro_ancho_caja, // Ancho de la caja que contiene la gráfico (por defecto 100.0)
parametro_alto_caja // Alto de la caja que contiene la gráfico (por defecto 100.0)
)
{
var cerrado=parametro_cerrado||false; // Valor booleano que indica si el trazado se cierra o no (por defecto false)
var ancho_caja=parametro_ancho_caja||100.0; // Ancho de la caja que contiene la gráfico (por defecto 100.0)
var alto_caja=parametro_alto_caja||100.0; // Alto de la caja que contiene la gráfico (por defecto 100.0)
var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG
var desplazamiento=[]; // Desplazamientos X e Y para posicionar el tiempo y el valor representado dentro del rango del gráfico
var escala=[]; // Coeficientes X e Y para calcular el tamaño al representar el gráfico
var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos)
var contador_valor=coordenada.length–1; // Variable para recorrer los valores (índice)
var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado
escala[0]=ancho_caja/tiempo_total_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
escala[1]=alto_caja/(Math.abs(valor_maximo–valor_minimo)+margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
desplazamiento[0]=coordenada[coordenada.length–1][0]–tiempo_total_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
desplazamiento[1]=margen_valor–valor_minimo; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
if(cerrado) // Si se dibuja un path (trazado) cerrado…
{
coordenadas_trazado+=ancho_caja+“,”+alto_caja+” L “; // …se empieza por la parte inferior de la caja
}
while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico…
{
posicion[0]=(coordenada[contador_valor][0]–desplazamiento[0])*escala[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal
posicion[1]=alto_caja–(coordenada[contador_valor][1]+desplazamiento[1])*escala[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG)
punto[contador_valor].setAttribute(“cx”,posicion[0]);
punto[contador_valor].setAttribute(“cy”,posicion[1]);
coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y
if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo…
{
coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que represntar, añadir una nueva línea (código L) para el próximo
}
else // Si se ha rebasado el margen izquierdo…
{
sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas)
}
contador_valor—; // Pasar al siguiente valor
}
if(cerrado) // Si se dibuja un trazado (path) cerrado…
{
coordenadas_trazado+=” L “+posicion[0]+“,”+alto_caja+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG
}
grafico.setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado
for(;contador_valor>=0;contador_valor—)
{
punto[contador_valor].setAttribute(“cx”,–10000);
punto[contador_valor].setAttribute(“cy”,0);
}
}
|
בוצעו שינויים בפונקציה actualizar_grafico()
כדי לקבל את הפונקציה החדשה actualizar_grafico_puntos()
הם אלה המודגשים בקוד של הדוגמה הקודמת. ראשית, בשורה 5, אנו לוקחים וקטור של עצמים SVG כפרמטר. וקטור זה יכיל את הסמלים שיש למקם מחדש בצמתים החדשים של הגרף.
בקווים 39 ו-40 מוקצות הקואורדינטות החדשות של המרכז, cx
y cy
, לאלו מהערכים שמיוצגים. אם הסמל לא היה מבוסס על המרכז, כנראה שיהיה צורך להוסיף היסט cx
חצי מהרוחב ובפנים cy
של חצי מהגובה כדי למקם אותם מחדש בדיוק על צומת הגרף.
בשורות 57 עד 61, הנקודות המתאימות לקואורדינטות שאינן מצוירות מכיוון שהן מנותקות על ידי הקצה השמאלי ממוקמות מחדש מחוץ לגרף. הקואורדינטה של cy
לאפס וזה של cx
לכל מספר שלילי (גדול מהנקודה עצמה) כך שהוא לא יוצג כאשר הוא חתוך, כמו החלק השמאלי של הגרף, על ידי החלון של SVG.
נהל את התרשים מאובייקט עם JavaScript
ניתן לשלב את כל הפעולות שהוסברו עד כה באובייקט כדי לנהל את הגרף בסגנון אופייני יותר לגרסאות החדשות של JavaScript. לחלופת יישום זו יתרון נוסף של פישוט השילוב של מספר גרפים, בעלי ערכים שונים, באותו דף אינטרנט.
לפני שנדון ביישום, הבה נסקור את הדרכים הנפוצות ביותר ליצור איתן אובייקטים JavaScript וכמה מהמוזרויות של הפונקציות המשפיעות על ההצעה לציור גרפיקה של חיישני IoT.
כבר הוסבר שהדרך החדשה ליצור אובייקטים ב JavaScript (זמין מאז גרסה 5 של ECMAScript) מורכב משימוש Object.create
, שאמור להתרגל להשתמש במקום ה"קלאסי" new
, שכמובן עדיין עובד כמו שצריך, אם כי מטרתו היא יותר לדמות את סגנון השפות עם אובייקטים מבוססי כיתה (JavaScript מבסס את היצירה של אובייקטים על אבות טיפוס) מאשר חלופה עובדת.
1
2
3
4
5
6
7
8
9
10
|
<!DOCTYPE html>
<html lang=“es”>
<head>
<meta charset=“utf-8”>
<title>Crear objetos con new o con Objetct.create</title>
<script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/crear_objetos.js”></script>
</head>
<body onload=“empezar();”>
</body>
</html>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
function Objeto_clasico(primero,segundo)
{
this.primero=primero||1;
this.segundo=segundo||2;
this.hacer_algo=function()
{
console.log(“El objeto clásico le saluda (primero=”+this.primero+” y segundo=”+this.segundo+“)”);
}
}
var Objeto_ES5=
{
primero:10,
segundo:20,
hacer_algo:
function()
{
console.log(“El objeto ES5 le saluda (primero=”+this.primero+” y segundo=”+this.segundo+“)”);
}
}
var objeto_clasico=new Objeto_clasico();
var objeto_ES5=Object.create(Objeto_ES5);
function empezar()
{
console.log(“Objeto clásico (“+objeto_clasico.primero+“,”+objeto_clasico.segundo+“)”);
console.log(“Objeto ES5 (“+objeto_ES5.primero+“,”+objeto_ES5.segundo+“)”);
objeto_clasico.hacer_algo();
objeto_ES5.hacer_algo();
}
|
הקוד הקודם מאפשר לך לזכור את ההבדלים בין יצירת האובייקטים עם Object.create
או עם new
. זה גם משמש להדגיש את זה, בעוד הפונקציה שבה האובייקט נוצר עם new
יכול להיות בכל מקום בקוד, האובייקט חייב כבר להתקיים לפני שניתן יהיה ליצור איתו מופע Object.create
(אובייקט ES5_Object אינו פונקציה).
בשורות 3 ו-4, כדי להגדיר ערך ברירת מחדל למאפיינים בפונקציה שיוצרת את האובייקט איתה new
, כל מאפיין מוקצה לערך של הארגומנט המתאים או (||
), אם לא הועברו טיעונים, כלומר, אם הם לא מוגדרים (undefined
), כפי שמעריכים את הנסיבות false
, מוקצה ערך ברירת המחדל.
ההקשר שבו מופעלת פונקציה JavaScript מעלה שני נושאים שחשוב לזכור ושעשויים לבלבל גם כשמשתמשים בשפת תכנות זו לאחר שעבדתם עם אחרים, כמו למשל C o C + +, במקרה שלנו. ההקשר כולל את המשתנים המוגדרים בהיקף הפונקציה (והגלובליים) אשר, אגב, מעלה מושג מעניין, ה"סגירות" שמבססות סגנון תכנות שלם ב JavaScript. עם זאת, ניתן היה לצפות כך this
, המתייחס לאובייקט כאשר משתמשים בו בתוך הקוד שמגדיר אותו, הקשר הביצוע בו הוא הוגדר נשמר אך זה שהוא משתמש בו הוא ההקשר שממנו נקראת הפונקציה. התנהגות זו שקופה ברוב המקרים, אך ישנן שתי נסיבות שבהן היא יכולה לבלבל: פונקציה המוגדרת בתוך פונקציה אחרת ושיטה הנקראת מאירוע אובייקט. window
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
var primero=“Primero global”;
var segundo=“Segundo global”;
var Contexto=
{
primero:“Primero en contexto”,
segundo:“Segundo en contexto”,
probar:
function()
{
console.log(“Primero en contexto: “+this.primero);
console.log(“Segundo en contexto: “+this.segundo);
function probar_dentro()
{
console.log(“Primero dentro: “+this.primero);
console.log(“Segundo dentro: “+this.segundo);
}
probar_dentro();
}
}
var probador=Object.create(Contexto);
probador.probar();
/*
Primero en contexto: Primero en contexto
Segundo en contexto: Segundo en contexto
Primero dentro: Primero global
Segundo dentro: Segundo global
*/
|
בעת ביצוע הקוד הקודם, הטקסט המוער בסופו מוצג במסוף. שני הקווים המסומנים משקפים התנהגות שיכולה לבלבל: ההקשר של ביצוע הפונקציה probar_dentro()
לא probar()
, כפי שניתן לצפות, אבל window
, המציג את המשתנים הגלובליים ולא את המאפיינים באותו שם. אם אינך רוצה התנהגות זו, פשוט צור משתנה בפונקציה ברמה הגבוהה ביותר והקצה אותו ל this
, כמו בקוד הבא.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
var primero=“Primero global”;
var segundo=“Segundo global”;
var Contexto=
{
primero:“Primero en contexto”,
segundo:“Segundo en contexto”,
probar:
function()
{
var esto=this;
console.log(“Primero en contexto: “+esto.primero);
console.log(“Segundo en contexto: “+esto.segundo);
function probar_dentro()
{
console.log(“Primero dentro: “+esto.primero);
console.log(“Segundo dentro: “+esto.segundo);
}
probar_dentro();
}
}
var probador=Object.create(Contexto);
probador.probar();
/*
Primero en contexto: Primero en contexto
Segundo en contexto: Segundo en contexto
Primero dentro: Primero en contexto
Segundo dentro: Segundo en contexto
*/
|
לשלוט בהקשר הביצוע כאשר מתודה נקראת מאירוע window
, למשל על ידי שינוי גודל חלון הדפדפן, ייחוד נוסף של JavaScript: אפשרות לתכנות "מפעלי פונקציות", כלומר פונקציות שמייצרות פונקציות אחרות, להחזיר אותן עם return
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var Contexto=
{
prueba:“objeto”, // La propiedad prueba es de los objetos Contexto
llamar:
function()
{
esto=this;
return function()
{
console.log(“H + “+(Date.now()–hora)+” Contexto: “+esto.prueba);
};
}
}
var prueba=“global”; // Esta variable prueba es global (window.prueba)
var hora=Date.now();
var probador=Object.create(Contexto);
var cosa=probador.llamar();
var pesadez=setInterval(cosa,3000);
setTimeout(function(){clearInterval(pesadez)},30100); // Desactivar la llamada periódica
|
בקוד לדוגמה למעלה, השיטה llamar()
של החפצים Contexto
זה לא עושה את העבודה אבל מחזיר פונקציה אנונימית שדואגת לזה. כדי לוודא שהכל עובד כמצופה, קיים משתנה גלובלי בעל שם זהה למאפיין שהפונקציה מציגה במסוף; אם ההקשר נכון, הערך של המאפיין יוצג ולא זה של המשתנה הגלובלי.
JavaScript נסו לתקן את סימני נקודה-פסיק שאנו משמיטים בסוף המשפטים. זה מאפשר סגנון כתיבה נינוח אך הוא חרב פיפיות שיש להתייחס אליה בזהירות. ברוב המקרים, כדי למנוע את ההשפעות הלא רצויות שזה מייצר בביטויים שתופסים מספר שורות, ניתן להשתמש בסוגריים או להקדים את הדרך שבה JavaScript יפרש את הקוד; לכן שורה 8 בדוגמה כוללת function
מאחורי return
, אם הייתי משתמש בשורה אחרת המשמעות הייתה שונה מאוד. לדעתי, הפתרון הכי קריא הוא להשתמש במשתנה ביניים (ניתן לבטל) כמו בגרסה הבאה; ברור שברגע שההתנהגות מובנת, ההחלטה מתאימה למתכנת.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
var Contexto=
{
prueba:“objeto”, // La propiedad prueba es de los objetos Contexto
llamar:
function()
{
esto=this;
var variable_auxiliar=
function()
{
console.log(“H + “+(Date.now()–hora)+” Contexto: “+esto.prueba);
};
return variable_auxiliar;
}
}
var prueba=“global”; // Esta variable prueba es global (window.prueba)
var hora=Date.now();
var probador=Object.create(Contexto);
var cosa=probador.llamar();
var pesadez=setInterval(cosa,3000);
setTimeout(function(){clearInterval(pesadez)},30100);
|
באותו מובן של הערכת ביטוי כפונקציה, כלומר החזרת פונקציה ולא הערך שהפונקציה מחזירה; בשורה 21 של הדוגמה האחרונה (זה היה בקו 19 של הקודמת) זה נעצר עם clearInterval
הפונקציה שנקראת עם setInterval
. כדי שזה יפעל במשך 30 שניות, העצירה נדחתה עם setTimeout
, שבתורו צריך פונקציה כארגומנט הראשון; לספק את הביצוע כפרמטר clearInterval
עם המשתנה שמכיל את הקריאה המחזורית (ולא את הפונקציה clearInterval
) בשביל זה נוצרת הפונקציה האנונימית בשורה האחרונה.
הבחירה בין כתיבת הקוד המשלב את הגדרת הפונקציה, קומפקטית יותר (כמו בשורה 21) או שימוש במשתנה עזר, לדעתי, קריא יותר (כמו בשורות 19 ו-20) משתנה מעט בביצועים ותלויה יותר בסגנון וקריאות עבור תחזוקה.
כדי לבדוק את הקוד, לפני שיש נתונים על השרת, אתה יכול להשתמש במחולל של ערכים אקראיים בטווח הרצוי או להכין טבלאות עם ערכים מבוקרים המדמים פעולה בתנאים הרצויים. הדוגמה הבאה משתמשת במחולל נתונים פשוט על פני כל הטווח, וזו הסיבה שהם נראים מעט מוגזמים.
כדי לבדוק, אתה יכול הורד את הקוד המלא של הדוגמה נוצר על ידי דף אינטרנט שנכתב ב HTML, הסגנון CSS והקוד JavaScript. האחרון הוא הרלוונטי ביותר, שכן הרכיבים האחרים הם רק תמיכה מינימלית, פשוטים מאוד ומפותחים הרבה יותר במאמרים בסעיפים המקבילים.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!DOCTYPE html>
<html lang=“es”>
<head>
<meta charset=“utf-8”>
<title>Temperatura</title>
<script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/grafico.js”></script>
<link rel=“stylesheet” href=“estilo.css” type=“text/css” media=“all”>
</head>
<body onload=“iniciar_grafico(‘grafico’,’valor’,’fecha’);”>
<div id=“temperatura”>
<div id=“ultimo_valor”>
<div id=“fecha”></div>
<div id=“valor”></div>
</div>
<div id=“grafico”></div>
</div>
</body>
</html>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
body
{
margin:0px 0px 0px 0px;
}
#ultimo_valor
{
box–sizing:border–box;
width:100%;
height:40px;
padding:8px 0px 8px 14px;
font–family:monospaced;
font–size:18px;
color:#205587;
background–color:#86A7D0;
}
#fecha
{
float:left;
margin–right:15px;
}
#valor
{
float:left;
}
#grafico
{
width:100%;
height:200px;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
|
var Grafico=
{
contenedor_svg:void(0), // Objeto SVG con el que se dibuja el gráfico. Se inicializa con void(0) para asegurarse de que es === undefined al principio
contenedor_html:void(0), // Objeto HTML que contiene al objeto SVG con el gráfico (seguramente un div). Se inicializa con void(0) para asegurarse de que es === undefined al principio
contenedor_valor:void(0), // Objeto HTML en el que se muestra el último valor cargado del servidor
contenedor_fecha:void(0), // Objeto HTML para mostrar la fecha del último valor
pagina_servidor:“ultimo_valor_sensor.php”, // página en la que se consultan los datos
consulta:“fr1”, // Valor del parámetro con el que se consulta el servidor. La URL sería algo como http://servidoriot.com/ultimo_valor_sensor.php?zona=fr1
NS:“http://www.w3.org/2000/svg”, // Nombre del espacio de nombres (name space NS)
MINIMO:0, // Índice del menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
OPTIMO:1, // Índice del mejor valor del parámetro medido. Valor deseado para el parámetro monitorizado. Sirve para tener una idea rápida de cómo de correcto es el estado del sistema
MAXIMO:2, // Índice del mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
maximo_puntos:0, // Número máximo de vértices representados (pueden dibujarse menos si se recortan por la parte izquierda)
valor:[], // Últimos valores cargados del servidor
trazado:[], // Trazados representados (en principio se trata de representar uno con relleno y otro con línea)
cerrado:true, // Cuando vale true se dibuja el perfil y el área (la línea del gráfico y una zona rellena debajo). Cuando vale false solo se dibuja la línea
simbolo:[], // Objetos SVG utilizados para marcar los vértices de la línea (círculos)
linea_referencia:[], // Líneas horizontales en los valores de referencia
texto_referencia:[], // Objetos texto que rotulan los valores de referencia
fondo_referencia:[], // Rectángulos bajo los textos de los valores de referencia
medida_caja_svg:[100.0,100.0], // Ancho,Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones ya que se dimensiona al 100% de su contenedor
correccion_caja_svg:[1.0,1.0], // Coeficientes para calcular la medida en el gráfico de un objeto del que se sabe el valor absoluto (Se usa el píxel como unidad)
tiempo_representado:0, // Milisegundos visibles en el gráfico empezando en el valor mayor (fecha y hora del último valor monitorizado)
valor_referencia:[0,0,0], // Valores de referencia [mínimo,óptimo,máximo]
margen_valor:0, // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo
escala_valor:[1.0,1.0], // Coeficientes que multiplican el valor monitorizado (y almacenado en el servidor) para calcular el representado según las dimensiones del gráfico
desplazamiento_valor:[0.0,0.0], // Desplazamiento que se suma al tiempo (X) y al valor (Y) para representarlo en el rango especificado para el gráfico
area_bajo_linea:true, // True para dibujar una zona rellena además del perfil (línea) del gráfico
medida_simbolo:[5,5], // Valor absoluto (en píxeles) del ancho y el alto del dibujo que se hace en los vértices de la línea del gráfico
tipografia:“sans-serif”, // Tipo de letra usado para rotular el gráfico (se usa solamente una tipografía por uniformidad)
altura_texto:10.0, // Altura de los textos en valor absoluto (píxeles). No todos los navegadores soportan todos los valores intermedios, será necesario coordinarlo con los valores que estén relacionados
escala_horizontal_texto:1, // Transformación que se aplica al texto para corregir la que implica la proporción del gráfico
color_fondo:“”, // Color de fondo del contenedor HTML del objeto SVG (Si es una cadena vacía no se cambia)
color_simbolos:“#000000”, // Color de los símbolos que se dibujan en los vértices de la línea del gráfico
color_referencia:[“#000000”,“#000000”,“#000000”], // Color de la referencia que indica el valor [mínimo,óptimo,máximo]
color_tipografia:“#FFFFFF”, // Color del tipo de letra con que se rotulan las referencias
grosor_trazado:4, // Grosor de la línea del gráfico
color_trazado:“#000000”, // Color de la línea del gráfico
color_relleno:“#000000”, // Color del relleno del gráfico
opacidad_relleno:1.0, // Opacidad del relleno del gráfico
grosor_referencia:0.5, // Grosor de la línea que se dibuja como referencia en valor absoluto (píxeles)
opacidad_referencia:1.0, // Opacidad de la línea de referencia. Si se dibuja sobre el gráfico con cierta transparencia permite ver el dibujo bajo ella
relleno_fondo_referencia:7.0, // Margen entre el fondo de la referencia y el texto medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
margen_fondo_referencia:10.0, // Separación de la referencia y el borde izquierdo del gráfico medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
ancho_fondo_referencia:50.0, // Medida horizontal del rectángulo que hace de fondo al texto de la referencia medido en valor absoluto
texto_valor:[“temperatura: “,” °C”], // Prefijo y sufico con los que se rotula el valor monitorizado
texto_hora:[“hora “,“”], // Prefijo y sufijo con los que se rotula la hora (cuando solamente se rotula la hora)
texto_fecha:[“”,“”], // Prefijo y sufijo con los que se rotula la fecha (cuando solamente se rotula la fecha)
texto_fecha_hora:[“el “,” a las “,” | “], // Prefijo con el que se rotula la hora, separador de fecha y hora y sufjo de la hora cuando se rotula la fecha y la hora
HORA:0, // Constante que representa la hora para el modo de la fecha/hora
FECHA:1, // Constante que representa la fehca para el modo de la fecha/hora
FECHA_Y_HORA:2, // Constante que representa rotular la fecha y la hora (para el modo de fecha/hora)
modo_fecha:2, // 0->hora, 1-> fecha 2-> fecha y hora
repetir:null, // Función llamada por setInterval
nueva_proporcion: // Calcular la nueva proporción (al iniciar y al redimensionar el contenedor del gráfico) para transformar el texto
function()
{
if(this.contenedor_svg!==undefined) // Se entiende que si no es undefined se ha asignado un objeto
{
if(this.contenedor_svg.nodeName===“svg”) // Si el objeto asignado es un SVG
{
this.escala_horizontal_texto=parseFloat(getComputedStyle(this.contenedor_svg).height)/parseFloat(getComputedStyle(this.contenedor_svg).width); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño
}
}
},
nueva_correccion_caja_svg:
function()
{
if(this.contenedor_svg!==undefined) // Se entiende que si no es undefined se ha asignado un objeto
{
if(this.contenedor_svg.nodeName.toLowerCase()===“svg”) // Si el objeto asignado es un SVG
{
this.correccion_caja_svg[0]=this.medida_caja_svg[0]/parseFloat(getComputedStyle(this.contenedor_svg).width);
this.correccion_caja_svg[1]=this.medida_caja_svg[1]/parseFloat(getComputedStyle(this.contenedor_svg).height);
}
}
},
nueva_escala:
function()
{
if(this.tiempo_representado>0) // Antes de inicializar el objeto el tiempo representado es cero
{
this.escala_valor[0]=this.medida_caja_svg[0]/this.tiempo_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
this.escala_valor[1]=this.medida_caja_svg[1]/(Math.abs(this.valor_referencia[this.MAXIMO]–this.valor_referencia[this.MINIMO])+this.margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
}
},
nuevo_desplazamiento:
function()
{
if(this.tiempo_representado>0) // Antes de inicializar el objeto el tiempo representado es cero
{
this.desplazamiento_valor[0]=this.valor[this.valor.length–1][0]–this.tiempo_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
this.desplazamiento_valor[1]=this.margen_valor–this.valor_referencia[this.MINIMO]; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
}
},
crear_svg: // Crear el objeto SVG dentro del objeto HTML
function()
{
if(this.contenedor_html!==null&&this.contenedor_html!==undefined) // Ya se ha asignado el objeto HTML
{
if(this.contenedor_html.nodeType===1) // Es un objeto HTML válido
{
// Color de fondo del objeto HTML
if(this.color_fondo!==“”)
{
this.contenedor_html.style.backgroundColor=this.color_fondo;
}
// Contenedor SVG
this.contenedor_svg=document.createElementNS(this.NS,“svg”); // Crear un objeto SVG
this.contenedor_svg.setAttribute(“width”,“100%”); // Ancho del objeto SVG
this.contenedor_svg.setAttribute(“height”,“100%”); // Alto del objeto SVG
this.contenedor_svg.setAttribute(“viewBox”,“0 0 “+this.medida_caja_svg[0]+” “+this.medida_caja_svg[1]); // Zona del SVG que se muestra
this.contenedor_svg.setAttribute(“preserveAspectRatio”,“none”); // No preservar la proporción para ocupar todo el objeto HTML que hace de contenedor
this.contenedor_html.appendChild(this.contenedor_svg); // Añadir al contenedor HTML el objeto SVG (que será el contenedor del gráfico)
// Trazado
this.trazado[0]=document.createElementNS(this.NS,“path”); // Crear el trazado que soporta el relleno
this.trazado[0].style.fill=this.color_relleno;
this.trazado[0].style.fillOpacity=this.opacidad_relleno;
this.trazado[0].style.stroke=“none”;
this.contenedor_svg.appendChild(this.trazado[0]); // Añadir el relleno al SVG
this.trazado[1]=document.createElementNS(this.NS,“path”); // Crear el trazado que soporta el perfil
this.trazado[1].setAttribute(“vector-effect”,“non-scaling-stroke”); // No mantener la proporción en el trazado (no deformarlo)
this.trazado[1].style.fill=“none”;
this.trazado[1].style.stroke=this.color_trazado;
this.trazado[1].style.strokeWidth=this.grosor_trazado;
//this.trazado[1].style.strokeOpacity=1.0; // La opacidad por defecto es 1.0
this.contenedor_svg.appendChild(this.trazado[1]); // Añadir el perfil al SVG
// Símbolos para los vértices
var ahora=Date.now()–30000; // Fecha y hora actual menos 30 segundos
for(var contador_vertices=0;contador_vertices<this.maximo_puntos;contador_vertices++)
{
this.valor[contador_vertices]=[];
this.valor[contador_vertices][0]=ahora; // Inicializar los valores a la fecha y hora actual menos 30 segundos
this.valor[contador_vertices][1]=this.valor_referencia[this.OPTIMO]; // Inicializar los valores al óptimo
this.simbolo[contador_vertices]=document.createElementNS(this.NS,“ellipse”); // Crear una elipse (círculo) para cada vértice
this.simbolo[contador_vertices].style.fill=this.color_simbolos;
this.simbolo[contador_vertices].style.fillOpacity=1.0;
this.simbolo[contador_vertices].style.stroke=“none”;
this.contenedor_svg.appendChild(this.simbolo[contador_vertices]); // Añadir el símbolo al SVG
}
//this.nuevo_desplazamiento(); // Necesario si se asignaran las alturas de las referencias
for(var contador_referencia=0;contador_referencia<this.valor_referencia.length;contador_referencia++)
{
// Línea de referencia
//var posicion_corregida=this.medida_caja_svg[1]-(this.valor_referencia[contador_referencia]+this.desplazamiento_valor[1])*this.escala_valor[1]; // Necesario si se asignaran las alturas de las referencias
this.linea_referencia[contador_referencia]=document.createElementNS(this.NS,“line”); // Crear la línea que representa el valor mínimo
this.linea_referencia[contador_referencia].style.stroke=this.color_referencia[contador_referencia];
this.linea_referencia[contador_referencia].style.strokeWidth=this.grosor_referencia;
this.linea_referencia[contador_referencia].style.strokeOpacity=this.opacidad_referencia;
this.linea_referencia[contador_referencia].setAttribute(“x1”,0.0); // Las líneas de referencia empiezan en el borde izquierdo…
this.linea_referencia[contador_referencia].setAttribute(“x2”,this.medida_caja_svg[0]); // …y terminan en el derecho
//this.linea_referencia[contador_referencia].setAttribute(“y1”,posicion_corregida); // Ambos extremos a la altura correspondiente a la referencia [mínimo,óptimo,máximo]
//this.linea_referencia[contador_referencia].setAttribute(“y2”,posicion_corregida); // Ambos extremos a la altura correspondiente a la referencia [mínimo,óptimo,máximo]
this.contenedor_svg.appendChild(this.linea_referencia[contador_referencia]);
// Rectángulo de fondo del texto que indica el valor de referencia
this.fondo_referencia[contador_referencia]=document.createElementNS(this.NS,‘rect’);
this.fondo_referencia[contador_referencia].style.fill=this.color_referencia[contador_referencia];
this.fondo_referencia[contador_referencia].style.fillOpacity=1.0;
this.fondo_referencia[contador_referencia].style.stroke=“none”;
this.contenedor_svg.appendChild(this.fondo_referencia[contador_referencia]);
// Texto de referencia
this.texto_referencia[contador_referencia]=document.createElementNS(this.NS,“text”); // Crear el texto para rotular la referencia
this.texto_referencia[contador_referencia].setAttribute(“font-family”,this.tipografia); // Asignar el tipo de letra
this.texto_referencia[contador_referencia].setAttribute(“fill”,this.color_tipografia); // Asignar el color
//this.texto_referencia[contador_referencia].textContent=””+(this.valor_referencia[contador_referencia]>=0?”+”:””)+this.valor_referencia[contador_referencia]; // Texto que se rotula (los valores máximo, óptimo y mínimo ya deben estar asignados)
this.contenedor_svg.appendChild(this.texto_referencia[contador_referencia]);
}
}
}
},
ajustar_prporcion:
function()
{
this.nueva_proporcion();
this.nueva_correccion_caja_svg();
var posicion_corregida;
for(var contador_vertices=0;contador_vertices<this.simbolo.length;contador_vertices++)
{
this.simbolo[contador_vertices].setAttribute(“rx”,this.medida_simbolo[0]*this.correccion_caja_svg[0]);
this.simbolo[contador_vertices].setAttribute(“ry”,this.medida_simbolo[1]*this.correccion_caja_svg[1]);
}
for(var contador_referencia=0;contador_referencia<this.valor_referencia.length;contador_referencia++)
{
posicion_corregida=this.medida_caja_svg[1]–(this.valor_referencia[contador_referencia]+this.desplazamiento_valor[1])*this.escala_valor[1];
this.fondo_referencia[contador_referencia].setAttribute(“x”,this.margen_fondo_referencia*this.correccion_caja_svg[0]);
this.fondo_referencia[contador_referencia].setAttribute(“y”,posicion_corregida–(this.altura_texto+this.relleno_fondo_referencia*2.0)*this.correccion_caja_svg[1]/2.0);
this.fondo_referencia[contador_referencia].setAttribute(“width”,this.ancho_fondo_referencia*this.correccion_caja_svg[0]);
this.fondo_referencia[contador_referencia].setAttribute(“height”,(this.altura_texto+this.relleno_fondo_referencia*2.0)*this.correccion_caja_svg[1]);
this.linea_referencia[contador_referencia].setAttribute(“y1”,posicion_corregida);
this.linea_referencia[contador_referencia].setAttribute(“y2”,posicion_corregida);
this.texto_referencia[contador_referencia].setAttribute(“transform”,“scale(“+this.escala_horizontal_texto+“,1.0)”);
this.texto_referencia[contador_referencia].setAttribute(“font-size”,this.altura_texto*this.correccion_caja_svg[1]);
this.texto_referencia[contador_referencia].setAttribute(“x”,(this.margen_fondo_referencia+this.relleno_fondo_referencia)*this.correccion_caja_svg[0]/this.escala_horizontal_texto);
this.texto_referencia[contador_referencia].setAttribute(“y”,posicion_corregida+this.altura_texto/2.0*this.correccion_caja_svg[1]);
this.texto_referencia[contador_referencia].textContent=(this.valor_referencia[contador_referencia]>=0?“+”:“”)+this.valor_referencia[contador_referencia]; // Texto que se rotula
}
},
rotar_valores:
function()
{
for(var contador_valor=0;contador_valor<this.valor.length–1;contador_valor++)
{
this.valor[contador_valor][0]=this.valor[contador_valor+1][0];
this.valor[contador_valor][1]=this.valor[contador_valor+1][1];
}
},
nuevo_valor_aleatorio:
function()
{
this.rotar_valores();
this.valor[this.valor.length–1][0]=this.valor[this.valor.length–2][0]+1000+Math.random()*1000; // El nuevo tiempo es el anterior más un segundo más un valor entre 0 y un segundo
this.valor[this.valor.length–1][1]=Math.random()*Math.abs(this.valor_referencia[this.MAXIMO]–this.valor_referencia[this.MINIMO])+this.valor_referencia[this.MINIMO]; // Un valor aleatorio entre el máximo y el mínimo
this.nuevo_desplazamiento(); // Cada nuevo valor cambia el desplazamiento en horizontal
this.dibujar_nuevo_valor(true);
this.dibujar_nuevo_valor(false);
this.rotular_nuevo_valor();
this.ritular_nueva_fecha();
},
cargar_nuevo_valor:
function()
{
var consulta=‘zona=”+this.consulta;
var resultado;
var ajax;
if(window.XMLHttpRequest)
{
ajax=new XMLHttpRequest(); // ajax=Object.create(XMLHttpRequest);
}
else // Versiones antiguas de MS Internet Explorer
{
ajax=new ActiveXObject(“Microsoft.XMLHTTP”);
}
ajax.onreadystatechange=
function()
{
if(ajax.readyState==4&&ajax.status==200&&ajax.responseType==“json”)
{
resultado=JSON.parse(ajax.responseText);
if(resultado.fecha>objeto_grafico.fecha[objeto_grafico.fecha.length–1])
{
this.rotar_valores();
this.valor[this.valor.length–1][0]=resultado.fecha;
this.valor[this.valor.length–1][1]=resultado.temperatura;
this.nuevo_desplazamiento(); // Cada nuevo valor cambia el desplazamiento en horizontal
this.dibujar_nuevo_valor(true);
this.dibujar_nuevo_valor(false);
this.rotular_nuevo_valor();
this.ritular_nueva_fecha();
}
}
}
ajax.open(“POST”,this.pagina_servidor);
ajax.setRequestHeader(“Method”,“POST “+this.pagina_servidor+” HTTP/1.1″);
ajax.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);
ajax.setRequestHeader(“Content-length”,consulta.length);
ajax.setRequestHeader(“Connection”,“close”);
ajax.send(consulta);
},
rotular_nuevo_valor:
function()
{
var valor_redondeado=Math.round(this.valor[this.valor.length–1][1]*100.0)/100.0;
this.contenedor_valor.innerHTML=this.texto_valor[0]+valor_redondeado+this.texto_valor[1];
},
ritular_nueva_fecha:
function()
{
var fecha_lectura=new Date(this.valor[this.valor.length–1][0]);
var texto_fecha=this.texto_hora[0];
texto_fecha+=(fecha_lectura.getHours()<10?“0”:“”)+fecha_lectura.getHours();
texto_fecha+=“:”;
texto_fecha+=(fecha_lectura.getMinutes()<10?“0”:“”)+fecha_lectura.getMinutes();
texto_fecha+=“:”;
texto_fecha+=(fecha_lectura.getSeconds()<10?“0”:“”)+fecha_lectura.getSeconds();
texto_fecha+=this.texto_hora[1];
this.contenedor_fecha.innerHTML=texto_fecha;
},
dibujar_nuevo_valor: // Dibujar el gráfico cuando llega un nuevo valor (que estará almacenado en el vector valor
function(cerrado)
{
// Si cerrado es undefined se evalúa a false, que se toma como valor por defecto
var contador_valor=this.valor.length–1; // Variable para recorrer los valores (índice)
var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos)
var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado
var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG
if(cerrado) // Si el trazado que se está dibujando está cerrado (no confundir con this.cerrado que determina si se dibujan los dos trazados, la línea y el relleno)
{
coordenadas_trazado+=this.medida_caja_svg[0]+“,”+this.medida_caja_svg[1]+” L “; // …se empieza por la parte inferior de la caja
}
while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico…
{
posicion[0]=(this.valor[contador_valor][0]–this.desplazamiento_valor[0])*this.escala_valor[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal
posicion[1]=this.medida_caja_svg[1]–(this.valor[contador_valor][1]+this.desplazamiento_valor[1])*this.escala_valor[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG)
this.simbolo[contador_valor].setAttribute(“cx”,posicion[0]);
this.simbolo[contador_valor].setAttribute(“cy”,posicion[1]);
coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y
if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo…
{
coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que representar, añadir una nueva línea (código L) para el próximo
contador_valor—; // Pasar al siguiente valor
}
else // Si se ha rebasado el margen izquierdo…
{
sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas)
}
}
if(cerrado) // Si se dibuja un trazado (path) cerrado…
{
coordenadas_trazado+=” L “+posicion[0]+“,”+this.medida_caja_svg[1]+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG
}
this.trazado[cerrado?0:1].setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado
for(;contador_valor>=0;contador_valor—)
{
this.simbolo[contador_valor].setAttribute(“cx”,–10000);
this.simbolo[contador_valor].setAttribute(“cy”,0);
}
}
}
var temperatura_frigorifico;
function iniciar_grafico(nombre_objeto_grafico,nombre_objeto_valor,nombre_objeto_fecha)
{
temperatura_frigorifico=Object.create(Grafico);
temperatura_frigorifico.contenedor_html=document.getElementById(nombre_objeto_grafico);
temperatura_frigorifico.contenedor_valor=document.getElementById(nombre_objeto_valor);
temperatura_frigorifico.contenedor_fecha=document.getElementById(nombre_objeto_fecha);
temperatura_frigorifico.color_fondo=“#A8C3EA”;
temperatura_frigorifico.color_trazado=“#205587”;
temperatura_frigorifico.grosor_trazado=3;
temperatura_frigorifico.color_relleno=“#205587”;
temperatura_frigorifico.opacidad_relleno=0.5;
temperatura_frigorifico.color_simbolos=“#205587”;
temperatura_frigorifico.color_referencia[Grafico.MINIMO]=“#621D87”;
temperatura_frigorifico.color_referencia[Grafico.OPTIMO]=“#1D8762”;
temperatura_frigorifico.color_referencia[Grafico.MAXIMO]=“#871E35”;
temperatura_frigorifico.grosor_referencia=1.5;
temperatura_frigorifico.opacidad_referencia=0.5;
temperatura_frigorifico.tipografia=“monospaced”;
temperatura_frigorifico.color_tipografia=“#A8C3EA”;
temperatura_frigorifico.cerrado=true; // Dibujar una línea y una zona rellena debajo (true por defecto)
temperatura_frigorifico.maximo_puntos=32; // Un espacio de 30 segundos representados con un intervalo mínimo de 1 segundo necesitan como máximo 31 puntos, se añade uno por seguridad (por si algún valor monitorizado estuviera por debajo del segundo)
temperatura_frigorifico.crear_svg();
temperatura_frigorifico.nueva_correccion_caja_svg();
temperatura_frigorifico.tiempo_representado=30000;
temperatura_frigorifico.valor_referencia[Grafico.MINIMO]=–5;
temperatura_frigorifico.valor_referencia[Grafico.OPTIMO]=5;
temperatura_frigorifico.valor_referencia[Grafico.MAXIMO]=10;
temperatura_frigorifico.margen_valor=3;
temperatura_frigorifico.nueva_escala();
temperatura_frigorifico.nuevo_desplazamiento(); // El desplazamiento se (re)calcula cada vez que se añade un valor
temperatura_frigorifico.ajustar_prporcion();
window.addEventListener(“resize”,function(){temperatura_frigorifico.ajustar_prporcion()}); // Versión moderna
temperatura_frigorifico.repetir=setInterval(function(){temperatura_frigorifico.nuevo_valor_aleatorio()},1000);
}
|
לפרסם תגובה