Створюйте та змінюйте графіку SVG даних із датчиків, підключених до IoT за допомогою JavaScript
У цій останній частині циклу статей про малювання графіка з даними датчиків, підключених до Інтернету речей, настав час поговорити про те, як створити або змінити за допомогою JavaScript малюнки у форматі SVG і деякі з елементів HTML які служать контейнером або представляють додаткову інформацію до графіки.
Передбачається, що цільові користувачі цього підручника мають профіль електроніки та комп’ютерного програмування. мікроконтролери, вони можуть бути не знайомі HTML, CSS o SVG; З цієї причини в попередніх частинах було зроблено короткий вступ до мови або відповідної технології. У цій останній частині підхід дещо інший, оскільки читачі напевно знають, як програмувати, можливо, що використовуючи мову C + + що як JavaScript, спільний базовий синтаксис з C і це можна взяти як посилання, щоб пропустити більшість базових концепцій програмування та, таким чином, зосередитися на відмінностях і конкретному використанні, яке нас цікавить для створення сенсорної графіки в IoT.
Назва дає ключ до першої відмінності: JavaScript Це мова програмування сценарій (дефіс) і як такий, він є інтерпретується, немає необхідності компілювати його; контекст, у якому сценарій (наприклад, веб-браузер) буде читати, перекладати та виконувати накази. Якщо бути точним, то в більшості випадків є компіляція під час виконання (JIT), але для процесу написання коду JavaScript Це не впливає на нас, ми просто пишемо код, і він може працювати.
Назва також містить першу плутанину: JavaScript не має жодного відношення до Java. Спочатку, коли він був розроблений Netscape для свого браузера він спочатку називався 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
|
У попередньому прикладі було оголошено змінну "thing" (без вказівки типу даних), потім присвоєно дані іншого типу, і вони консультуються з 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
; Тобто об'єкт існує, але без вартості; змінна, яка посилається на неї, не матиме a typeof
undefined
sino object
. Об’єкт також може бути порожнім, тобто не нульовим, але не мати жодних властивостей.
в визначити об'єкт в 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 року (наприклад, Час Unix або POSIX але виражені в мілісекундах замість секунд) або вказуючи окремі значення року, місяця, дня, години...
Об'єкт включає повну серію методи запиту або встановлення дати й часу:
-
now()
Повертає поточну дату й час, виражені в мілісекундах з 1 січня 1970 року -
getTime()
|setTime()
Отримує або змінює, відповідно, значення часу в мілісекундах з 1 січня 1970 року. ВикористанняvalueOf()
, який є методом, присутнім у більшості об’єктів, також отримується значення відповідного об’єкта Date, наприкладgetTime()
з Час Unix або POSIX виражається в мс. -
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;
}
|
Звичайно, у попередньому прикладі змінна "результат" взагалі не була потрібна, але це хороший привід запам'ятати область змінної, який працює так, як ви очікуєте: змінна "result" існує лише у функції "double". в 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».
Доступ до об’єктної моделі документа (DOM) за допомогою JavaScript
від JavaScript Ви маєте доступ до вмісту веб-сторінки, на якій вона працює, а також до деяких аспектів браузера, який відображає цю сторінку, але не до системних ресурсів. Структура даних, яка підтримує властивості та методи, з яких здійснюється доступ JavaScript частина об'єкта вікна, зокрема, зміст об’єкта (документ HTML) відповідає об’єкту document
. Хоча це іноді використовується для ясності, немає необхідності передувати window методам або властивостям, щоб посилатися на них, достатньо, наприклад, використовувати 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
(НС для простір імен). Як пояснюється при розмові про формат SVG, простір імен, який йому відповідає (для поточної версії), є http://www.w3.org/2000/svg
, який слід передати createElementNS
як аргумент разом із типом елемента, svg
, в цьому випадку.
Una альтернатива innerHTML
щоб додати текст як вміст до елемента документа HTML це метод createTextNode()
об'єкт document
. За допомогою цієї альтернативи ви можете створити новий текст (до якого пізніше можна отримати доступ, якщо його присвоїти змінній), який включено в дерево об’єктів за допомогою методу appendChild()
. Як альтернатива appendChild()
, який додає новий вміст у кінець того, що вже існує у вузлі, до якого він додається, ви можете використовувати метод insertBefore()
, який додає новий об’єкт перед існуючим. Носити insertBefore()
замість appendChild()
надає метод, який служить, наприклад, для сортувати нові об'єкти перед існуючими коли елемент має бути попереду іншого (як у списку) або покривати або бути покритим у графічній структурі, у якій є елементи ближче до переднього плану чи фону.
Реагувати на події за допомогою JavaScript
Коли шлях о використовувати веб-сторінку як контейнер для графіків підключених датчиків IoT це було використано onload
На етикетці <body>
щоб почати малювати графік. Це властивість, пов'язане з кодом об'єктів HTML, відноситься до Події JavaScript. Як уже пояснювалося, він виконує функцію, коли сторінка завантажується. Хоча це було пов'язано з кодом HTML щоб більше пам’ятати, це можна було б записати в коді JavaScript як body.onload=dibujar;
буття 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;
буття 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 через те, наскільки агресивним (нав’язливим) є закриття веб-сторінки діалоговим вікном.
У програмі, написаній для a мікроконтролер невеликої серії (наприклад, на тарілці Arduino Uno) зазвичай використовують глобальні змінні, як у попередньому прикладі в JavaScript, оскільки код короткий і не особливо заплутаний, оскільки часто функції реалізуються ad hoc і оскільки використання глобальних змінних дає змогу передбачити використання пам’яті дуже простим та інтуїтивно зрозумілим способом, що є критичним у системах з невеликою кількістю ресурсів . Натомість en JavaScript Зазвичай використання глобальних змінних зводиться до мінімуму. оскільки йому не потрібно поспішати з використанням пам’яті, оскільки він працює нормально на a центральний процесор з ресурсами, значно перевершуючими ресурси a MCU, тому що він, ймовірно, співіснуватиме з великою кількістю стороннього коду, з яким він повинен працювати без втручання, і оскільки це відкрита система, майбутній контекст виконання неможливо передбачити (програма мікроконтролер small повністю визначає його роботу без додавання додаткового коду, коли він працює) і тому, що розміри додатків можуть ускладнити читання, якщо код не інкапсулює його роботу, роблячи методи максимально самодостатніми.
Математичні операції з об’єктом JavaScript Math
В об'єкт згруповані математичні операції більш складного математичного розрахунку Math
. Цей об’єкт використовується безпосередньо, не потрібно створювати його екземпляр для використання методів або властивостей (констант), які він включає.
Math.abs(n)
Абсолютне значення параметра nMath.acos(n)
Аркосинус параметра n (результат у радіанах)Math.asin(n)
Арксинус параметра n (результат у радіанах)Math.atan(n)
Арктангенс параметра n (результат у радіанах)Math.atan2(n,m)
Арктангенс н/м (результат у радіанах)Math.ceil(n)
Округліть параметр до найближчого цілого числа вгоруMath.cos(α)
Косинус параметра α (α в радіанах)Math.E
e число (≃2.718281828459045)Math.exp(n)
e піднесено до параметра n: enMath.floor(n)
Округліть параметр n до найближчого цілого числа внизMath.log(n)
Натуральний логарифм (за основою e) параметра nMath.LN2
Натуральний логарифм (за основою e) від 2 (≃0.6931471805599453)Math.LN10
Натуральний логарифм (за основою e) від 10 (≃2.302585092994046)Math.LOG2E
Логарифм e за основою 2 (≃1.4426950408889634)Math.LOG10E
Логарифм e за основою 10 (≃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 Ви можете побачити приклад інфраструктури для керування інформацією з боку сервера, яку надають пристрої, підключені до Інтернету речей. У цій серії статей сервер використовується як ресурс Apache з якого можна використовувати мову програмування PHP отримати доступ до бази даних MySQL o MariaDB. На серверах, які використовуються для підтримки 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);
}
|
У третьому рядку попереднього прикладу готується запит, який буде зроблено на сервер, в якому буде переданий аргумент «зона», значенням якого буде назва або код моніторингового місця, оскільки інформація про в одній базі даних можуть співіснувати різні датчики (наприклад, термометри, які вимірюють температуру в різних кімнатах). Очікується, що параметр, переданий у попередню функцію, об’єкт із даними діаграми, міститиме властивість із назвою кімнати («name_suffix»).
Між рядками 7 і 14 попереднього коду об’єкт XMLHttpRequest
який зберігається у змінній "ajax". Перш ніж вибрати спосіб створення об'єкта, ви шукаєте window
якщо XMLHttpRequest
був недоступний (те, що сталося в старих версіях провідника Microsoft, і хоча він значно відстає, він служить прикладом альтернатив для створення об’єкта за допомогою (більш рідного) синтаксису. Object.create
o new
подібно до інших об’єктно-орієнтованих мов.
Щоб мати можливість негайно керувати відповіддю, код, який її обробляє, готується в рядках з 15 по 26 перед тим, як надсилати запит серверу.
Шлях зробити запит HTTP до сервера складається з відкрити з'єднання з open
із зазначенням типу та сторінки (необов'язково ім'я користувача та пароль), підготувати заголовки протоколу с setRequestHeader
y відправити запит з send
. Заголовок HTTP Content-length
вам знадобиться знати довжину запиту (кількість символів), яка обчислюється за допомогою length
.
Коли запит AJAX готовий, функція, пов’язана з подією, виконується onreadystatechange
. Замість призначення функції в попередньому прикладі на льоту визначається анонімна функція, яка керуватиме отриманням даних, що надходять із сервера. Перш за все, у рядку 18 перевіряється статус запиту «завершено», що відповідає значенню 4
майна readyState
, що статус "OK" для протокол 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 px і висота 150 px встановлено в стилі (те, що в остаточній версії буде доцільно зробити з класом і документом 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);
}
|
Дати коментар