Генерирайте и модифицирайте SVG графики на данни от сензори, свързани към IoT с JavaScript
В тази последна част от поредицата статии за рисуване графики с данни от сензори, свързани с Интернет на нещата, време е да поговорим за това как да генерираме или модифицираме с JavaScript чертежи във формат SVG и някои от елементите HTML които служат като контейнер или които представят допълнителна информация към графиките.
Целевите потребители на този урок трябва да формират профил по електроника и компютърно програмиране. микроконтролери, те може да не са запознати с HTML, CSS o SVG; Поради тази причина в предишните части беше направено кратко въведение в езика или съответната технология. В тази последна част подходът е малко по-различен, тъй като читателите със сигурност знаят как да програмират, възможно е използването на езика C + + какво как JavaScript, споделя основен синтаксис с C и може да се използва като справка, за да пропуснете повечето от основните концепции за програмиране и по този начин да се съсредоточите върху разликите и специфичната употреба, която ни интересува, за да създадем сензорна графика в IoT.
Името дава представа за първата разлика: JavaScript Това е език за програмиране писменост (тире) и като такъв е тълкува, няма нужда да го компилираме; контекстът, в който писменост (уеб браузър, например) ще прочете, преведе и изпълни поръчките. За да бъдем точни, в повечето случаи има a компилация по време на изпълнение (JIT), но за процеса на писане на код JavaScript Това не ни засяга, ние просто пишем кода и той може да работи.
Името съдържа и първото объркване: JavaScript няма ни най-малко отношение към Ява. Първоначално, когато е разработен 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
сино 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
) o 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, но могат да бъдат присвоени по-големи стойности, които ще се натрупат в общата дата и час, така че, подобно на останалите методи за получаване, тя служи за увеличаване на стойността на обекта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 о EN 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
. Въпреки че понякога се използва за яснота, не е необходимо да се предшества прозорец към методите или свойствата, за да се отнасят към тях, достатъчно е, например, да се използва 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
, в такъв случай.
а алтернатива на 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)
Арктангенс на 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
Логаритъм с основа 2 от e (≃1.4426950408889634)Math.LOG10E
Логаритъм с основа 10 от e (≃0.4342944819032518)Math.max(a,b,c,…)
Най-голямата стойност от списъка с предадени параметриMath.min(a,b,c,…)
Най-малката стойност от списъка с предадени параметриMath.PI
Число π (≃3.141592653589793)Math.pow(n,m)
Първият параметър n е повишен до втория параметър m: nmMath.random()
(Почти) произволно число между 0.0 и 1.0Math.round(n)
Закръглете параметър n до най-близкото цяло числоMath.sin(α)
Синус на параметър α (α в радиани)Math.sqrt(n)
Корен квадратен от параметър nMath.SQRT1_2
Корен квадратен от 1/2 (≃0.7071067811865476)Math.SQRT2
Корен квадратен от 2 (≃1.4142135623730951)Math.tan(α)
Тангенс на параметър α (α в радиани)
Заредете данни от сървъра с AJAX
Методът, следван за изчертаване на информацията, съхранявана в IoT, се състои в зареждане на данните от сървъра от време на време и преначертаване на графиката, с която те са представени. За четене на данни от сървъра се използва технология AJAX (асинхронен JavaScript и XML) чрез обект XMLHttpRequest
de JavaScript. Начертаването на графиката с данни се извършва чрез повторно използване на обект SVG което вече е в кода HTML и това съдържа диаграма, чиито координати са променени, за да съответстват на новите заредени данни.
В примера на това предложение, в допълнение към актуализирането на чертежа, се актуализира и текст на уеб страницата, който показва датата и стойността на последните измерени данни за всяка графика.
От страната на сървъра има база данни, която съдържа информацията че сензорите, свързани към IoT, са наблюдавали. Тази база данни се чете от заявката за обект XMLHttpRequest
отговаряйки с информация, кодирана в JSON формат, въпреки че името на използвания метод предполага връзка с формата XML.
В първия урок polaridad.es за IoT съхранение на данни Можете да видите пример за инфраструктура за управление от страна на сървъра на информацията, предоставена от устройства, свързани към Интернет на нещата. В тази серия от статии като ресурс се използва сървър 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 от предишния код, the обект XMLHttpRequest
който се съхранява в променливата "ajax". Преди да изберете как да създадете обекта, вие търсите window
ако XMLHttpRequest
не беше наличен (нещо, което се случи в старите версии на Explorer на Microsoft и въпреки че е далеч назад, служи като пример за алтернативи за създаване на обекта с помощта на (по-родния) синтаксис) Object.create
o new
, подобно на това на други обектно-ориентирани езици.
За да можете да управлявате незабавно отговора, кодът, който го обработва, се подготвя в редове от 15 до 26, преди да отправи заявката към сървъра.
Пътят на изпълни заявката HTTP към сървъра се състои от отворете връзка с open
посочване на тип и страница (по желание потребителско име и парола), подгответе заглавките на протокола с setRequestHeader
y изпрати заявката с send
. Заглавката HTTP Content-length
ще трябва да знаете дължината на заявката (броя знаци), която се изчислява с помощта на length
.
Когато искането AJAX е готов, функцията, свързана със събитието, се изпълнява onreadystatechange
. Вместо да се присвоява функция, в предишния пример се дефинира анонимна функция в движение, която ще управлява приемането на данни, пристигащи от сървъра. Първо, на ред 18 се проверява дали статусът на заявката е „завършен“, което съответства на стойността 4
от имота readyState
, че състоянието е „ОК“ на HTTP протокол (код 200
), които могат да бъдат получени от имота status
и че пристигналите данни са JSON формат, консултиране на имота responseType
.
След като се провери дали състоянието на отговора е според очакванията, в ред 20 от предишния пример създава обект с резултата, преобразувайки текста JSON. Отговорът предоставя дата, която трябва да бъде върната, това ни позволява да видим дали резултатът, който сървърът изпраща, вече е бил представен в графиката, което се проверява на ред 21. Ако данните са нови, на ред 23 Функцията, която отговаря за преначертаването на графиката с новата информация.
Идеята, когато се предлага този метод на четене, е данните да се опресняват много често. Ако представената информация съответства на дългосрочен период (като температурите за ден или седмица), може да се приложи първоначална заявка, която събира всички налични данни и след това такава, подобна на тази в примера, която ги актуализира в периодичният кореспондент.
Генерирайте произволни данни за тестване
Когато цялата сървърна и клиентска инфраструктура е готова, функция като тази в предишния раздел ще отговаря за четенето на данните и чертането на графиката с тях, но Във фазата на тестване може да е по-практично да се използват произволни числа в рамките на контролиран диапазон за да видите дали написаният код е правилен. Следната функция може да служи като пример за получаване на данни при изграждането на крайното приложение.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
function consultar_ultimo_valor_sensores
(
objeto_grafico,// Objeto dentro del elemento SVG que representa el trazado
valor_maximo, // Valor máximo
valor_minimo, // Valor mínimo
margen_valor // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
)
{
// La forma más genérica es usar objetos
/*
var nuevo_valor_inventado=
{
fecha:Math.round(Date.now()+Math.random()*1000),
temperatura:Math.random()*Math.abs(valor_maximo-valor_minimo)+valor_minimo
};
*/
// En este caso concreto, como son pocos datos y con una estructura concreta es más práctico usar un vector
var nuevo_valor_inventado=
[
Math.round(Date.now()+Math.random()*1000),
Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo
];
redibujar_grafico(objeto_grafico,nuevo_valor_inventado);
}
|
Вместо да чете информацията от база данни, примерът по-горе ги генерира на случаен принцип и ги предава на функцията, която отговаря за изчертаването на графиката. Измислените данни са вектор, образуван от дата, изразена като стойност в милисекунди, моментът на запис на информацията от сензора и наблюдаваните данни, които са между максимална стойност и минимална стойност.
В този пример, когато генерирате дата, тя може да бъде забавена до една секунда (1000 милисекунди) по отношение на датата към момента на изобретението. Като Math.random()
генерира число между 0.0 и 1.0, умножаването му по 1000 произвежда число между 0 и 1000, което след това се преобразува в цяло число. По същия начин стойността се получава чрез умножаване на произволното число по диапазона (максимум минус минимум) и добавяне на минимума.
Начертайте графиката на IoT сензорите със SVG диаграма
Тъй като видяхме как можем да получим стойностите, които искаме да представим (температура, в примера) и тяхното временно местоположение, което може да бъде изразено заедно под формата на координати, примерът по-долу показва функция за чертане на път който свързва тези точки и по желание цветна област, ограничена от тази линия в горната част. Резултатът ще бъде като следното изображение.
Хоризонталната ос (X) на графиката представлява времето, а вертикалната ос (Y) - стойностите, които сензорите, свързани към IoT, са наблюдавали. Хоризонталният интервал е няколко секунди, тъй като в това предложение графиката се актуализира много често (на всяка секунда, например), за да предостави информация почти в реално време за състоянието на сензорите.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
function actualizar_grafico
(
grafico, // Objeto SVG con el que se dibuja la gráfico
coordenada, // Matriz con las coordenadas del trazado formada por pares [tiempo,valor] ordenados primero el más antiguo (tiempo menor) último el más nuevo
tiempo_total_representado, // Tiempo total representado por la gráfico (en milisegundos)
valor_maximo, // Valor máximo aceptable antes de emitir una alarma
valor_minimo, // Valor mínimo aceptable antes de emitir una alarma
margen_valor, // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
parametro_cerrado, // Valor booleano que indica si el trazado se cierra o no (por defecto false)
parametro_ancho_caja, // Ancho de la caja que contiene la gráfico (por defecto 100.0)
parametro_alto_caja // Alto de la caja que contiene la gráfico (por defecto 100.0)
)
{
var cerrado=parametro_cerrado||false; // Valor booleano que indica si el trazado se cierra o no (por defecto false)
var ancho_caja=parametro_ancho_caja||100.0; // Ancho de la caja que contiene la gráfico (por defecto 100.0)
var alto_caja=parametro_alto_caja||100.0; // Alto de la caja que contiene la gráfico (por defecto 100.0)
var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG
var desplazamiento=[]; // Desplazamientos X e Y para posicionar el tiempo y el valor representado dentro del rango del gráfico
var escala=[]; // Coeficientes X e Y para calcular el tamaño al representar el gráfico
var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos)
var contador_valor=coordenada.length–1; // Variable para recorrer los valores (índice)
var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado
escala[0]=ancho_caja/tiempo_total_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
escala[1]=alto_caja/(Math.abs(valor_maximo–valor_minimo)+margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
desplazamiento[0]=coordenada[coordenada.length–1][0]–tiempo_total_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
desplazamiento[1]=margen_valor–valor_minimo; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
if(cerrado) // Si se dibuja un path (trazado) cerrado…
{
coordenadas_trazado+=ancho_caja+“,”+alto_caja+” L “; // …se empieza por la parte inferior de la caja
}
while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico…
{
posicion[0]=(coordenada[contador_valor][0]–desplazamiento[0])*escala[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal
posicion[1]=alto_caja–(coordenada[contador_valor][1]+desplazamiento[1])*escala[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG)
coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y
if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo…
{
coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que represntar, añadir una nueva línea (código L) para el próximo
contador_valor—; // Pasar al siguiente valor
}
else // Si se ha rebasado el margen izquierdo…
{
sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas)
}
}
if(cerrado) // Si se dibuja un trazado (path) cerrado…
{
coordenadas_trazado+=” L “+posicion[0]+“,”+alto_caja+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG
}
grafico.setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado
}
|
В предишния код има два интересни аспекта, първо изчислението, което позволява адаптирайте диапазона от стойности, които са представени и второ, имотно строителство d
което показва координатите на точките на оформлението (path
).
За да се адаптира диапазонът от представени стойности, те се преместват от минимум и се мащабират, така че видимата величина да съответства на размера на графиката. В случай на време, отместването се получава чрез изваждане на диапазона, който искате да покажете от най-дългото време (датата и часът, които са най-близки до текущия) (20 секунди в примера). Изместването на температурните стойности е това на долния диапазон (един градус) минус най-ниската стойност, така че данните, показани по-долу, са най-близки до най-ниската позволена стойност, но оставят граница, която ни позволява да оценим тези, които правят , пас
Коефициентът, който умножава времевите стойности за получаване на хоризонталните координати на графиката, се получава чрез разделяне на общата ширина на графиката (100 единици в примера) на представения времеви диапазон (20 секунди в примера). За да се получи коефициентът със скаларните температурни стойности, трябва да се помни, че представеният диапазон варира от граница под минималната стойност до граница над максималната, един градус и в двата случая. По този начин коефициентът на вертикалната скала е резултат от разделянето на височината на графиката (100 единици в примера) на максималната стойност, минус минималната плюс горното и долното поле. Тъй като тези стойности могат да се развият напълно при отрицателни температури, ние използваме Math.abs()
да се използва абсолютната стойност на разликата.
Собствеността d
на обекта path
Конструира се чрез свързване на координатите на точките в текст. Всяка двойка координати се предхожда от код SVG L
, който чертае линия от текущата позиция до абсолютна стойност, която е посочена от координатите. Стойностите X и Y са разделени със запетаи и всяка операция SVG е отделен с интервал от следващия.
За да стартирате оформлението, използвайте кода M
(преместете се в абсолютна координата). В случай на затворена и запълнена диаграма, започвате от долния десен ъгъл, в случай на отворена диаграма, която чертае профила на данните, започвате с последната представена стойност (най-новата). За завършване на затвореното оформление се използва кодът Z
добавяйки като последна точка тази, която има същата стойност на координатата X като последната точка на линията и като координата Y най-малката представена стойност.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
function dibujar_grafico()
{
var tiempo_mostrado=20000; // Se representan 20 segundos (20000 milisegundos)
var valor_maximo=10; // 10 grados sobre cero
var valor_minimo=–5; // Cinco grados bajo cero
var fecha_hora=Date.now(); // Hora actual
var matriz_de_coordenadas_de_prueba=[]; // Preparar el vector de coordenadas
for(var contador=0;contador<20;contador++)
{
fecha_hora+=500+1500*Math.random(); // Añadir medio segundo a la hora anterior y entre 0 y segundo y medio aleatoriamente
matriz_de_coordenadas_de_prueba[contador]=[]; // Preparar el siguiente punto del vector de coordenadas
matriz_de_coordenadas_de_prueba[contador][0]=fecha_hora; // En la coordenada horizontal, situar la hora
matriz_de_coordenadas_de_prueba[contador][1]=Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo; // En la coordenada vertical situar el valor del sensor
}
actualizar_grafico
(
document.getElementById(“relleno_temperatura”), // Trazado para el relleno definido en el código HTML
matriz_de_coordenadas_de_prueba,
tiempo_mostrado,
valor_maximo, // Diez grados de valor máximo
valor_minimo, // Cinco grados bajo cero como valor mínimo
1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente
true // Cerrar el trazado para representar el área rellena
);
actualizar_grafico
(
document.getElementById(“linea_temperatura”), // Trazado para el relleno definido en el código HTML
matriz_de_coordenadas_de_prueba,
tiempo_mostrado,
valor_maximo, // Diez grados de valor máximo
valor_minimo, // Cinco grados bajo cero como valor mínimo
1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente
false // No cerrar el trazado para representar la linea que une los puntos que representan las temperaturas
);
}
|
В този пример функцията dibujar_grafico()
, което е извикването при зареждане на страницата, получава първоначалните стойности за тестване (не последната стойност в реално време) и подготвя диапазона, в който данните ще бъдат изобразени: 20 секунди (20000 15 ms) хоризонтално и 5°C в вертикално от -10°C до +XNUMX°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 о EN 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);
}
|
Публикувай коментар