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